添加上传图片两张

This commit is contained in:
wangyilin 2025-01-03 11:36:46 +08:00
parent a46d6e2f24
commit 0d9beb2686
8 changed files with 419 additions and 253 deletions

View File

@ -9,16 +9,53 @@
<menu-fold-outlined v-else class="trigger" @click="() => (collapsed = !collapsed)"/>
</div>
<div class="margin-right flex-center">
<a-dropdown :trigger="['click']" placement="bottomRight" :arrow="{ pointAtCenter: true }">
<a-avatar/>
<template #overlay >
<a-menu style="width: 100px;text-align: center">
<a-menu-item key="0" @click="logout"> 退出登录</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
</div>
</template>
<script setup lang="ts">
import {MenuFoldOutlined, MenuUnfoldOutlined} from "@ant-design/icons-vue";
import {ExclamationCircleOutlined, MenuFoldOutlined, MenuUnfoldOutlined} from "@ant-design/icons-vue";
import router from "@/router";
import {useUserStore} from "@/stores/modules/userStore.ts";
import {Modal} from "ant-design-vue";
import {createVNode} from "vue";
const collapsed = defineModel<boolean>('collapsed')
const userStore = useUserStore()
// const logout = ()=>{
// localStorage.removeItem("baUserStore");
// router.push('/login')
// userStore.resetUserInfo()
// }
const logout = () => {
Modal.confirm({
title: '确定要退出登录么?',
icon: createVNode(ExclamationCircleOutlined),
async onOk() {
//
await userStore.resetUserInfo();
//
await router.push({
path: '/login'
})
},
async onCancel() {
console.log('Cancel');
},
class: 'test',
});
};
</script>
<style scoped lang="scss">

View File

@ -5,92 +5,118 @@
height="80%"
v-else
:src="minioBaseUrl + modelValue"
alt="avatar"/>
alt="avatar"
/>
<a-button class="btn-success" @click="selectFile">{{ btnLabel }}</a-button>
<input id="myFileInput" type="file" style="display: none" ref="fileInput" />
<input :id="id" type="file" style="display: none" ref="fileInput" />
</div>
</template>
<script setup lang="ts">
import { message } from "ant-design-vue";
import { onMounted, onUnmounted, ref } from "vue";
import {generateSimpleObjectName, getResignedObjectUrl} from "@/utils/minioUtil";
import {
generateSimpleObjectName,
getResignedObjectUrl,
} from "@/utils/minioUtil";
import axios, { CancelTokenSource } from "axios";
import { convertFileSizeToStr } from "@/utils/index.ts";
const minioBaseUrl = __APP_ENV.VITE_APP_MINIO_URL
const minioBaseUrl = __APP_ENV.VITE_APP_MINIO_URL;
const modelValue = defineModel<string>('value')
const props = withDefaults(defineProps<{
parentDir?: string,
allowedExtensions?: string[],
maxSize?: number,
width?: string | number,
height?: string | number,
btnLabel?: string
}>(), {
parentDir: '',
allowedExtensions: () => ['jpg', 'jpeg', 'png', 'gif'],
const modelValue = defineModel<string>("value");
const props = withDefaults(
defineProps<{
parentDir?: string;
allowedExtensions?: string[];
maxSize?: number;
width?: string | number;
height?: string | number;
btnLabel?: string;
id: string;
}>(),
{
parentDir: "",
allowedExtensions: () => ["jpg", "jpeg", "png", "gif"],
maxSize: 1024 * 1024 * 4,
width: '150px',
height: '150px',
btnLabel: '选择图片'
})
width: "150px",
height: "150px",
btnLabel: "选择图片",
id: "myFileInput",
}
);
const uploading = ref(false)
const percent = ref(0)
let cancelToken: CancelTokenSource | null = null
const uploadUrl = ref()
const uploading = ref(false);
const percent = ref(0);
let cancelToken: CancelTokenSource | null = null;
const uploadUrl = ref();
const fileInput = ref(null);
const selectFile = () => {
document.getElementById('myFileInput')?.click()
}
document.getElementById(props.id)?.click();
};
async function inputFileListener(this: HTMLInputElement) {
const selectedFile: File = this.files?.[0] as File;
const fileExtension = selectedFile.name?.split('.').pop().toLowerCase() as string;
if (!props.allowedExtensions.includes(fileExtension)) {
return message.error(`错误:不支持的文件格式,目前支持:【${props.allowedExtensions}`)
}
const fileExtension = selectedFile.name
?.split(".")
.pop()
.toLowerCase() as string;
if (!props.allowedExtensions.includes(fileExtension)) {
return message.error(
`错误:不支持的文件格式,目前支持:【${props.allowedExtensions}`
);
}
const isMax = selectedFile.size > props.maxSize;
if (isMax) {
return message.error(`文件大小超出限制,最大支持:【${convertFileSizeToStr(props.maxSize)}`);
return message.error(
`文件大小超出限制,最大支持:【${convertFileSizeToStr(props.maxSize)}`
);
}
cancelToken?.cancel();
percent.value = 0;
uploading.value = true;
const objectName = generateSimpleObjectName(selectedFile.name, props.parentDir)
uploadUrl.value = await getResignedObjectUrl(__APP_ENV.VITE_APP_MINIO_BUCKET, objectName)
cancelToken = axios.CancelToken.source()
const objectName = generateSimpleObjectName(
selectedFile.name,
props.parentDir
);
uploadUrl.value = await getResignedObjectUrl(
__APP_ENV.VITE_APP_MINIO_BUCKET,
objectName
);
cancelToken = axios.CancelToken.source();
await axios.put(uploadUrl.value, selectedFile, {
cancelToken: cancelToken.token,
onUploadProgress: (progressEvent) => {
percent.value = (progressEvent.loaded / (progressEvent.total as number) * 100 | 0)
}
})
modelValue.value = '/' + __APP_ENV.VITE_APP_MINIO_BUCKET + objectName;
percent.value =
((progressEvent.loaded / (progressEvent.total as number)) * 100) | 0;
},
});
modelValue.value = "/" + __APP_ENV.VITE_APP_MINIO_BUCKET + objectName;
uploading.value = false;
}
const fileDelete = () => {
uploadUrl.value = ''
fileInput.value.value = ''
modelValue.value = ''
}
uploadUrl.value = "";
fileInput.value.value = "";
modelValue.value = "";
};
onMounted(() => {
document.getElementById('myFileInput')?.addEventListener('change', inputFileListener);
})
document
.getElementById(props.id)
?.addEventListener("change", inputFileListener);
});
onUnmounted(() => {
document.getElementById('myFileInput')?.removeEventListener('change', inputFileListener);
})
defineExpose({fileDelete})
document
.getElementById(props.id)
?.removeEventListener("change", inputFileListener);
});
defineExpose({ fileDelete });
</script>
<style scoped lang="scss">

View File

@ -20,6 +20,8 @@ router.beforeEach(async (to, from, next) => {
Modal.destroyAll();
//判断访问的是不是登录页
const userStore = useUserStore();
console.log(to.path.toLocaleLowerCase())
console.log(userStore)
if (to.path.toLocaleLowerCase() === '/login' && userStore.getTokenInfo?.value) {
//如果已登录 且访问login页面 直接返回当前页面
await message.warn('当前已登录,请先退出账号');

View File

@ -20,7 +20,7 @@ export const useUserStore = defineStore({
getTokenInfo: (state): TokenInfo => state.tokenInfo as TokenInfo,
},
persist: {
key: "baUserStore", //spUserStore
key: "baUserStore",
storage: window.localStorage,
paths: ["tokenInfo"],
}

View File

@ -6,7 +6,8 @@ export interface formDatePort {
telephone:string,
administrativeDivisionCodes:string,
address:string,
nature:string
nature:string,
securityServiceLicence:string
}
export interface statusPort {

View File

@ -11,6 +11,7 @@ export interface serviceProjectSaveOrUpdateParams extends BaseTableRowRecord {
twoType: BaseEnum<any>,
outsourceName:string,
isFiling:BaseEnum<number>,
securityServiceContract:string,
idNumber: string,
serviceArea: number,
buildingTotal: number,

View File

@ -2,9 +2,15 @@
<!-- 背景色覆盖整个页面并且能够正常滚动内容同时固定内层内容的位置 -->
<body style="margin: 0; padding: 0; overflow: auto">
<!-- 背景色层 -->
<div class="bg-gray-100" style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: -1"></div>
<div
class="bg-gray-100"
style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: -1"
></div>
<!-- 内容层 -->
<div class="flex justify-center" style="position: relative; margin: 20px auto">
<div
class="flex justify-center"
style="position: relative; margin: 20px auto"
>
<!-- 卡片盒子 -->
<div class="w-full max-w-3xl p-6 bg-white rounded-xl shadow-md">
<a-tabs v-model:activeKey="activeKey" :tabBarGutter="300" centered>
@ -21,7 +27,11 @@
<a-form-item label="名称" name="name">
<a-input :allowClear="true" v-model:value="formDate.name" />
</a-form-item>
<a-form-item :allowClear="true" label="统一社会编码" name="socialCode">
<a-form-item
:allowClear="true"
label="统一社会编码"
name="socialCode"
>
<a-input v-model:value="formDate.socialCode" />
</a-form-item>
<a-form-item label="公司性质" name="nature">
@ -39,20 +49,49 @@
/>
</a-form-item>
<a-form-item label="营业执照" name="businessLicense">
<SingleImageFileUpload v-model:value="formDate.businessLicense" ref="fileUpload"></SingleImageFileUpload>
<SingleImageFileUpload
type="businessLicense"
btnLabel="营业执照上传"
v-model:value="formDate.businessLicense"
ref="fileUpload"
></SingleImageFileUpload>
</a-form-item>
<a-form-item label="备案证/许可证" name="securityServiceLicence">
<div style="display: flex">
<SingleImageFileUpload
v-model:value="formDate.securityServiceLicence"
ref="permitUpload"
id="securityServiceLicenceInput"
></SingleImageFileUpload>
<div style="color: red; text-align: left">
自行招用单位备案证或保安许可证
</div>
</div>
</a-form-item>
<a-form-item label="法人名称">
<a-input :allowClear="true" v-model:value="formDate.legalPersonInfo" />
<a-input
:allowClear="true"
v-model:value="formDate.legalPersonInfo"
/>
</a-form-item>
<a-form-item label="法人手机号码">
<a-input :allowClear="true" v-model:value="formDate.telephone" />
<a-input
:allowClear="true"
v-model:value="formDate.telephone"
/>
</a-form-item>
<a-form-item label="详细地址">
<a-input :allowClear="true" v-model:value="formDate.address" />
</a-form-item>
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
<a-button type="primary" html-type="submit" style="width: 100px">确认</a-button>
<a-button style="width: 100px;margin-left: 10px" @click="resetForm">重置表单</a-button>
<a-button type="primary" html-type="submit" style="width: 100px"
>确认</a-button
>
<a-button
style="width: 100px; margin-left: 10px"
@click="resetForm"
>重置表单</a-button
>
</a-form-item>
</a-form>
</a-tab-pane>
@ -61,123 +100,149 @@
:label-col="labelCol"
:wrapper-col="wrapperCol"
:model="statusDate"
layout="horizontal">
<a-form-item label="统一社会编码" name="onlyCode" :rules="[{ required: true, message: '请输入统一社会编码进行查询' }]">
<a-input :allowClear="true" v-model:value="statusDate.onlyCode"></a-input>
layout="horizontal"
>
<a-form-item
label="统一社会编码"
name="onlyCode"
:rules="[
{ required: true, message: '请输入统一社会编码进行查询' },
]"
>
<a-input
:allowClear="true"
v-model:value="statusDate.onlyCode"
></a-input>
</a-form-item>
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
<a-button type="primary" html-type="submit" style="width: 100px" @click="getCheckStatus">确认</a-button>
<a-button
type="primary"
html-type="submit"
style="width: 100px"
@click="getCheckStatus"
>确认</a-button
>
</a-form-item>
</a-form>
</a-tab-pane></a-tabs>
</a-tab-pane></a-tabs
>
</div>
</div>
</body>
</template>
<script setup lang="ts">
import {ref, onMounted,createVNode,h} from 'vue';
import type { Rule } from 'ant-design-vue/es/form';
import type { ShowSearchType } from 'ant-design-vue/es/cascader';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { ref, onMounted, createVNode, h } from "vue";
import type { Rule } from "ant-design-vue/es/form";
import type { ShowSearchType } from "ant-design-vue/es/cascader";
import { ExclamationCircleOutlined } from "@ant-design/icons-vue";
import api from "@/axios";
import {CascaderProps, message, Modal} from 'ant-design-vue';
import { CascaderProps, message, Modal } from "ant-design-vue";
import SingleImageFileUpload from "@/components/upload/SingleImageFileUpload.vue";
import { useRouter } from "vue-router";
import { formDatePort, statusPort } from "@/types/views/enterprise.ts";
const activeKey = ref('1');
const labelCol = { style: { width: '120px' } };
const activeKey = ref("1");
const labelCol = { style: { width: "120px" } };
const wrapperCol = { span: 14 };
const administrativeDivisionTree = ref<TreeNodeVo<string>[]>([])
const administrativeDivisionTree = ref<TreeNodeVo<string>[]>([]);
const formDateRef = ref();
const fileUpload = ref()
const router = useRouter()
const fileUpload = ref();
const permitUpload = ref();
const router = useRouter();
const formDate = ref<formDatePort>({
name:'',
socialCode:'',
businessLicense:'',
legalPersonInfo:'',
telephone:'',
administrativeDivisionCodes:'',
address:'',
nature:''
})
name: "",
socialCode: "",
businessLicense: "",
legalPersonInfo: "",
telephone: "",
administrativeDivisionCodes: "",
address: "",
nature: "",
securityServiceLicence: "",
});
const statusDate = ref<statusPort>({
onlyCode:'',
unitOptType:'SECURITY_UNIT'
})
onlyCode: "",
unitOptType: "SECURITY_UNIT",
});
const rules: Record<string, Rule[]> = {
name: [
{ required: true, message: '请输入姓名', trigger: 'change' },
],
name: [{ required: true, message: "请输入姓名", trigger: "change" }],
socialCode: [
{ required: true, message: '请输入社会编码', trigger: 'change' },
],
nature:[
{ required: true, message: '请填写公司性质', trigger: 'change' },
{ required: true, message: "请输入社会编码", trigger: "change" },
],
nature: [{ required: true, message: "请填写公司性质", trigger: "change" }],
businessLicense: [
{ required: true, message: '请上传营业执照', trigger: 'change' },
{ required: true, message: "请上传营业执照", trigger: "change" },
],
securityServiceLicence: [
{ required: true, message: "请上传备案证/许可证", trigger: "change" },
],
administrativeDivisionCodes: [
{ required: true, message: '请选择行政区划', trigger: 'change' },
]
{ required: true, message: "请选择行政区划", trigger: "change" },
],
};
// 1
const DivisionTree = async () => {
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:'0'})
administrativeDivisionTree.value = resp.data as TreeNodeVo<string>[]
const resp = await api.get<TreeNodeVo<string>[]>(
"/common/administrativeDivisionByParentCode",
{ parentCode: "0" }
);
administrativeDivisionTree.value = resp.data as TreeNodeVo<string>[];
};
}
const loadData: CascaderProps['loadData'] = async selectedOptions => {
const loadData: CascaderProps["loadData"] = async (selectedOptions) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:targetOption.value})
const resp = await api.get<TreeNodeVo<string>[]>(
"/common/administrativeDivisionByParentCode",
{ parentCode: targetOption.value }
);
targetOption.loading = false;
targetOption.children = [administrativeDivisionTree.value,...resp.data]
targetOption.children = [administrativeDivisionTree.value, ...resp.data];
};
//
const transformData = (val: any, tree: any) => {
for (let i = 0; i < val.length - 1; i++) {
tree.forEach(async (item: any) => {
if (item.value === val[i]) {
item.children = []
let data = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:val[i]})
item.children = [];
let data = await api.get<TreeNodeVo<string>[]>(
"/common/administrativeDivisionByParentCode",
{ parentCode: val[i] }
);
item.children = data.data.map((items: any) => {
return {
label: items.label,
value:items.value
}
})
transformData(val,item.children)
}
})
value: items.value,
};
});
transformData(val, item.children);
}
});
}
};
// 2
const filter: ShowSearchType['filter'] = (inputValue, path) => {
return path?.some(option => option.label.toLowerCase().indexOf(inputValue?.toLowerCase()) > -1);
const filter: ShowSearchType["filter"] = (inputValue, path) => {
return path?.some(
(option) =>
option.label.toLowerCase().indexOf(inputValue?.toLowerCase()) > -1
);
};
//
const searchAdministrativeDivisionTree = (e: Array<string>) => {
formDate.value.administrativeDivisionCodes = e as any
}
formDate.value.administrativeDivisionCodes = e as any;
};
//
const onFinish = async () => {
//
await formDateRef.value.validate()
await formDateRef.value.validate();
const legalPersonInfo = {
name: formDate.value.legalPersonInfo,
telephone:formDate.value.telephone
}
telephone: formDate.value.telephone,
};
const securityUnitRegisterParams = {
name: formDate.value.name,
socialCode: formDate.value.socialCode,
@ -185,59 +250,72 @@ const onFinish = async ()=>{
legalPersonInfo: legalPersonInfo,
nature: formDate.value.nature,
administrativeDivisionCodes: formDate.value.administrativeDivisionCodes,
address:formDate.value.address
}
const resp = await api.post('/common/securityUnitRegister',securityUnitRegisterParams)
message.success(resp.message)
fileUpload.value.fileDelete()
await resetForm()
}
address: formDate.value.address,
securityServiceLicence: formDate.value.securityServiceLicence,
};
const resp = await api.post(
"/common/securityUnitRegister",
securityUnitRegisterParams
);
message.success(resp.message);
fileUpload.value.fileDelete();
permitUpload.value.fileDelete();
await resetForm();
};
//
const resetForm = async () => {
await formDateRef.value.resetFields()
await formDateRef.value.resetFields();
formDate.value = {
name:'',
socialCode:'',
businessLicense:'',
legalPersonInfo:'',
telephone:'',
administrativeDivisionCodes:'',
address:'',
nature:''
}
fileUpload.value.fileDelete()
}
name: "",
socialCode: "",
businessLicense: "",
legalPersonInfo: "",
telephone: "",
administrativeDivisionCodes: "",
address: "",
nature: "",
securityServiceLicence: "",
};
fileUpload.value.fileDelete();
permitUpload.value.fileDelete();
};
//
const getCheckStatus = async () => {
const indexCheckStatusParams = {
onlyCode: statusDate.value.onlyCode,
unitOptType:statusDate.value.unitOptType
}
const resp = await api.post<dataStatus>('/management/getCheckStatus',indexCheckStatusParams)
showConfirm(resp.data)
}
unitOptType: statusDate.value.unitOptType,
};
const resp = await api.post<dataStatus>(
"/management/getCheckStatus",
indexCheckStatusParams
);
showConfirm(resp.data);
};
const showConfirm = (columnsDate: dataStatus) => {
if (columnsDate.checkStatus.value === 0) {
Modal.success({
title: `审核通过`,
icon: createVNode(ExclamationCircleOutlined),
content: h('div', {}, [
h('div', `账号:${columnsDate.account}`),
h('div', `密码:${columnsDate.password}`),
h('div', `${columnsDate.remark}`)
content: h("div", {}, [
h("div", `账号:${columnsDate.account}`),
h("div", `密码:${columnsDate.password}`),
h("div", `${columnsDate.remark}`),
]),
okText:'跳转',
okText: "跳转",
async onOk() {
await router.push({
path:'/login',
await router
.push({
path: "/login",
query: {
account: columnsDate.account,
password:columnsDate.password
}
}).then(()=>{})
password: columnsDate.password,
},
})
.then(() => {});
},
onCancel() {},
});
@ -248,13 +326,12 @@ const showConfirm = (columnsDate:dataStatus) => {
content: `${columnsDate.remark}`,
});
}
};
onMounted(async () => {
await DivisionTree()
await transformData(formDate.value.administrativeDivisionCodes,administrativeDivisionTree.value)
})
await DivisionTree();
await transformData(
formDate.value.administrativeDivisionCodes,
administrativeDivisionTree.value
);
});
</script>

View File

@ -19,7 +19,8 @@
@ok="submit"
@cancel="closeModal"
>
<FormProMax ref="formRef" v-model:value="formParams" :form-item-options="formItemOptions"/>
<FormProMax ref="formRef" v-model:value="formParams" :form-item-options="formItemOptions">
</FormProMax>
</a-modal>
</div>
@ -37,6 +38,7 @@ import {FormExpose} from "ant-design-vue/es/form/Form";
import {FormProMaxItemOptions} from "@/types/components/form";
import FormProMax from "@/components/form/FormProMax.vue";
import {message} from "ant-design-vue";
import SingleImageFileUpload from "@/components/upload/SingleImageFileUpload.vue";
type TableProps = TableProMaxProps<serviceProjectSaveOrUpdateParams> //
@ -85,6 +87,7 @@ const formParams = ref<{
outsourceName?:string,
isFiling:number,
idNumber?: string,
securityServiceContract?:string,
serviceArea?:number,
buildingTotal?:number,
houseTotal?:number,
@ -116,7 +119,6 @@ const columns: TableProps['columns'] = [
title: '企事业单位名称',
width: 150,
ellipsis: true,
},
{
dataIndex:'type',
@ -146,10 +148,19 @@ const columns: TableProps['columns'] = [
title: '保安服务许可证',
width:170
},
{
dataIndex:'securityServiceContract',
title:'保安服务合同',
width:200,
customRender({text}) {
return <a-image width={100} height={100} src={__APP_ENV.VITE_APP_MINIO_URL + text}></a-image>
},
ellipsis: true,
},
{
dataIndex:'serviceArea',
title:'服务区域面积',
width:120
width:120,
},
{
dataIndex:'buildingTotal',
@ -225,6 +236,7 @@ const columns: TableProps['columns'] = [
formParams.value. securityUserTotal = record.securityUserTotal
formParams.value.enterprisesUnitId = record.enterprisesUnitName
formParams.value.administrativeDivisionCodes = record.enterprisesUnitAdministrativeDivisionCodes
formParams.value.securityServiceContract = record.securityServiceContract
}}>编辑</a-button>
</a-space>
@ -347,9 +359,16 @@ const formItemOptions = ref<FormProMaxItemOptions<serviceProjectSaveOrUpdatePara
idNumber: {
type: 'input',
label: '保安服务许可证',
// componentsProps:{
// disabled:idNumberDisabled as any
// }
},
securityServiceContract:{
type:'custom',
label: '保安服务合同',
required: true,
customRender: () =>
<div>
<SingleImageFileUpload height={200} v-model:value={formParams.value.securityServiceContract} />
<div class="securityServiceContract">只需提供体现签订合同的单位和执行合同起止时间的页面即可两张合在一起进行上传</div>
</div>
},
serviceArea:{
type:'inputNumber',
@ -432,6 +451,7 @@ const submit = async()=>{
staffTotal:formParams.value.staffTotal,
securityUserTotal:formParams.value.securityUserTotal,
remark: formParams.value.remark,
securityServiceContract:formParams.value.securityServiceContract
}
const resp = await api.post('/m3/sp/add_upd',serviceProjectSaveOrUpdateParams)
message.success(resp.message)
@ -471,6 +491,8 @@ onMounted(async ()=>{
</script>
<style scoped lang="scss">
<style lang="scss">
.securityServiceContract{
color: red;
}
</style>