refactor: 合并 biz_support_unit/biz_execution_unit/biz_service_org 为 biz_org, 删除 2 张孤儿表, 改 biz_person.work_unit → org_id FK
主要改动: - SQL: biz_support_unit → biz_org, 加 org_type, 加 business_nature; 删 biz_execution_unit, biz_service_org - SQL: biz_project.support_unit_id/name + service_org_id/name → org_id/name/type - SQL: biz_meeting.support_unit_name → org_name - SQL: biz_person.work_unit → org_id (FK) - 后端: 新 BizOrg entity/mapper/service/controller - 后端: BizProject/BizMeeting 字段重命名 - 后端: 删 12 个 BizSupportUnit/BizExecutionUnit/BizServiceOrg Java 文件 - 后端: BizPersonImportVO.workUnit → orgName (导入时查 biz_org 取 org_id) - 后端: BizAuthController.registerExecutor 完整实现 (原 registerSupplier stub) - 前端: 新 admin/Orgs.vue + manager/Orgs.vue (原 SupportUnits) - 前端: RegisterExecutor.vue (原 RegisterSupplier, 单页 2 步) - 前端: sponsor/executor/manager 多个文件 workUnit → orgId/orgName 重命名 - 前端: 统一 supplier → executor, 业务命名 sponsor(赞助方) / executor(执行方=供应商) - 前端: 全工程 execution → executor (company type / role / person unit_type)
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
<!--
|
||||
身份证正反面上传控件 (仿 hwt-code gr-id-card, 适配 PC 后台)
|
||||
用法 (双 v-model):
|
||||
<id-card-uploader
|
||||
v-model:front-url="form.idCardFrontUrl"
|
||||
v-model:back-url="form.idCardBackUrl"
|
||||
:dir="'ry8080/idcard/'"
|
||||
/>
|
||||
|
||||
readonly=true 时不显示上传按钮, 点击 thumb 弹 el-image 预览
|
||||
上传走 OSS 直传 (utils/oss.js 的 uploadToOss)
|
||||
-->
|
||||
<template>
|
||||
<div class="ht-image-upload-content" :class="{ 'has-file': frontUrl }" @click="handleClick('front')">
|
||||
<div v-if="!frontUrl && !readonly" class="ht-image-placeholder-wrap">
|
||||
<img class="ht-image-placeholder" :src="frontPlaceholder" />
|
||||
</div>
|
||||
<div v-else-if="!frontUrl && readonly" style="width: 90px;height: 60px; background: #f2f4f5;"></div>
|
||||
<img v-else-if="!readonly" class="ht-image" :src="frontUrl" />
|
||||
<el-image v-else :src="frontUrl" style="width: 90px;height: 60px; display: flex; justify-content: center; align-items: center; background: #f2f4f5;"
|
||||
:preview-src-list="[frontUrl]"></el-image>
|
||||
</div>
|
||||
<div class="ht-image-upload-content" :class="{ 'has-file': backUrl }" style="margin-left:20px" @click="handleClick('back')">
|
||||
<div v-if="!backUrl && !readonly" class="ht-image-placeholder-wrap">
|
||||
<img class="ht-image-placeholder" :src="backPlaceholder" />
|
||||
</div>
|
||||
<div v-else-if="!backUrl && readonly" style="width: 90px;height: 60px; background: #f2f4f5;"></div>
|
||||
<img v-else-if="!readonly" class="ht-image" :src="backUrl" />
|
||||
<el-image v-else :src="backUrl" style="width: 90px;height: 60px; display: flex; justify-content: center; align-items: center; background: #f2f4f5;"
|
||||
:preview-src-list="[backUrl]"></el-image>
|
||||
</div>
|
||||
<!-- hidden file input triggered by thumb click -->
|
||||
<input
|
||||
type="file"
|
||||
ref="fileInputFront"
|
||||
accept="image/*"
|
||||
style="display:none"
|
||||
@change="(e) => onFileChange(e, 'front')"
|
||||
/>
|
||||
<input
|
||||
type="file"
|
||||
ref="fileInputBack"
|
||||
accept="image/*"
|
||||
style="display:none"
|
||||
@change="(e) => onFileChange(e, 'back')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { uploadToOss } from '@/utils/oss'
|
||||
|
||||
const props = defineProps({
|
||||
frontUrl: { type: String, default: '' },
|
||||
backUrl: { type: String, default: '' },
|
||||
dir: { type: String, default: 'ry8080/idcard/' },
|
||||
readonly: { type: Boolean, default: false },
|
||||
frontPlaceholder: { type: String, default: '/images/id-front.png' },
|
||||
backPlaceholder: { type: String, default: '/images/id-back.png' }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:frontUrl', 'update:backUrl'])
|
||||
|
||||
const fileInputFront = ref(null)
|
||||
const fileInputBack = ref(null)
|
||||
const uploading = ref({ front: false, back: false })
|
||||
|
||||
function handleClick(side) {
|
||||
if (props.readonly) return
|
||||
if (uploading.value[side]) return
|
||||
const el = side === 'front' ? fileInputFront.value : fileInputBack.value
|
||||
el?.click()
|
||||
}
|
||||
|
||||
async function onFileChange(e, side) {
|
||||
const file = e.target.files?.[0]
|
||||
e.target.value = ''
|
||||
if (!file) return
|
||||
if (!file.type.startsWith('image/')) {
|
||||
ElMessage.warning('只能上传图片文件')
|
||||
return
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 10) {
|
||||
ElMessage.warning('图片大小不能超过 10MB')
|
||||
return
|
||||
}
|
||||
uploading.value[side] = true
|
||||
try {
|
||||
const url = await uploadToOss(file, props.dir)
|
||||
if (side === 'front') {
|
||||
emit('update:frontUrl', url)
|
||||
} else {
|
||||
emit('update:backUrl', url)
|
||||
}
|
||||
ElMessage.success(side === 'front' ? '身份证正面已上传' : '身份证反面已上传')
|
||||
} catch (err) {
|
||||
ElMessage.error(err.message || '上传失败')
|
||||
} finally {
|
||||
uploading.value[side] = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ht-image-upload-content {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 4px;
|
||||
width: 90px;
|
||||
height: 60px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ht-image-upload-content:hover {
|
||||
border-color: #1890ff;
|
||||
}
|
||||
.ht-image-upload-content.has-file {
|
||||
border-style: solid;
|
||||
border-color: #52c41a;
|
||||
}
|
||||
.ht-image-placeholder-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.ht-image-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.ht-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f2f4f5;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user