This commit is contained in:
qiuyuan 2025-11-06 10:17:27 +08:00
commit db3428381b
13 changed files with 250 additions and 195 deletions

View File

@ -13,6 +13,7 @@ VITE_ROUTER_HISTORY=hash
# api
VITE_API_BASIC='http://10.10.1.39:8040'
VITE_API_UPLOAD='http://115.239.217.220:9458'
VITE_API_HTTP=/api/v1/
# storage
VITE_STORAGE_NAMESPACE = gin-admin_local_

View File

@ -13,6 +13,6 @@ VITE_ROUTER_HISTORY=hash
# api
VITE_API_BASIC=/
VITE_API_HTTP=/api/v1/
VITE_API_UPLOAD='http://115.239.217.220:9458'
# storage
VITE_STORAGE_NAMESPACE=admin_pre_

View File

@ -13,6 +13,6 @@ VITE_ROUTER_HISTORY=hash
# api
VITE_API_BASIC=/
VITE_API_HTTP=/api/v1/
VITE_API_UPLOAD='http://115.239.217.220:9458'
# storage
VITE_STORAGE_NAMESPACE=ginadmin_

View File

@ -1,5 +1,6 @@
import request from '@/utils/request'
import axios from 'axios'
import { config } from '@/config'
// 获取地区
export const getRegion = (params) => request.basic.get('/region', params)
@ -9,7 +10,12 @@ export const getDictByType = (type) => request.basic.get(`/api/v1/dictionaries?c
export const getAreaList = (params) => request.basic.get('/api/v1/areas', params)
export const getChildAreaList = (id) => request.basic.get(`/api/v1/areas/${id}`)
//上传图片
export const uploadFile=(params)=>request.basic.post('/api/v1/upload',params,{
// export const uploadFile=(params)=>request.basic.post('/upload',params,{
// headers: {
// 'Content-Type': 'multipart/form-data'
// },
// })
export const uploadFile=(params)=>axios.post(`${config('http.apiUpload')}/upload`,params,{
headers: {
'Content-Type': 'multipart/form-data'
},

View File

@ -143,8 +143,8 @@ const handleCustomRequest = async (options) => {
formData.append('file', file);
const { data } = await apis.common.uploadFile(formData);
const fullUrl = config('http.apiBasic') + data;
const fullUrl = config('http.apiUpload') + data.saved_path;
console.log('上传成功文件URL', fullUrl);
//
onSuccess({
uid: file.uid,
@ -155,7 +155,7 @@ const handleCustomRequest = async (options) => {
}, file);
//
emit('uploadSuccess', data);
emit('uploadSuccess', { url: fullUrl });
} catch (err) {
onError(err);
message.error('上传失败');

View File

@ -1,44 +1,38 @@
<template>
<a-input
v-model:value="currentValue"
class="x-upload x-upload-input"
:allow-clear="allowClear"
@input="onInput">
<template #addonAfter>
<a-button
v-if="loading"
loading>
{{ loadingBtnText }}
</a-button>
<a-upload
v-else
:show-upload-list="false"
:custom-request="customRequest">
<a-button>{{ btnText }}</a-button>
:multiple="multiple"
@change="handleChange"
v-model:file-list="fileList"
:custom-request="handleCustomRequest"
:show-upload-list="{ showRemoveIcon: allowClear }"
>
<a-button>
<upload-outlined></upload-outlined>
{{ loading ? loadingBtnText : btnText }}
</a-button>
</a-upload>
</template>
</a-input>
</template>
<script setup>
import { Form } from 'ant-design-vue'
import { Form, message } from 'ant-design-vue'
import { onMounted, ref, watch } from 'vue'
import apis from '@/apis'
import { config } from '@/config'
defineOptions({
name: 'XUploadInput',
})
const fileList = ref([])
const loading = ref(false)
/**
* 文件上传
* @property {string} modelValue v-model
* @property {string} btnText 按钮内容默认选择文件
* @property {string} loadingBtnText 上传中按钮内容默认上传中
* @property {boolean} allowClear 允许清空默认true
* 组件属性
*/
const props = defineProps({
modelValue: {
type: String,
default: '',
type: Array,
default: () => []
},
btnText: {
type: String,
@ -52,83 +46,157 @@ const props = defineProps({
type: Boolean,
default: true,
},
multiple: {
type: Boolean,
default: false,
}
})
const emit = defineEmits(['update:modelValue', 'change'])
const emit = defineEmits(['update:modelValue', 'change', 'uploadSuccess', 'uploadError'])
const { onFieldChange } = Form.useInjectFormItemContext()
const loading = ref(false)
const currentValue = ref('')
watch(
() => props.modelValue,
(val) => {
if (currentValue.value !== val) {
currentValue.value = val
/**
* 初始化文件列表
*/
const initFileList = () => {
if (props.modelValue && props.modelValue.length > 0) {
fileList.value = props.modelValue.map(url => ({
uid: `preview-${Date.now()}-${Math.random()}`,
name: url.substring(url.lastIndexOf('/') + 1),
status: 'done',
url: url
}))
} else {
fileList.value = []
}
}
)
}
onMounted(() => {
currentValue.value = props.modelValue
initFileList()
})
/**
* 内容发生改变
* @param e
* 监听外部值变化
*/
function onInput(e) {
trigger(e.target.value)
}
watch(() => props.modelValue, (newVal) => {
// fileList URL
const doneUrls = fileList.value
.filter(f => f.status === 'done' && f.url)
.map(f => f.url)
if (JSON.stringify(newVal) !== JSON.stringify(doneUrls)) {
initFileList()
}
}, { deep: true })
/**
* 自定义上传
* @param info
* 自定义上传请求
*/
async function customRequest(info) {
const { file } = info
const handleCustomRequest = async (options) => {
const { file, onProgress, onSuccess, onError } = options
//
loading.value = true
setTimeout(() => {
loading.value = false
currentValue.value = file.name
trigger(currentValue.value)
}, 2000)
//
// loading.value = true
// const { code, data } = await apis.common.upload({
// file,
// })
// loading.value = false
// if (config('http.code.success') === code) {
// currentValue.value = data?.src
// trigger(currentValue.value)
// }
try {
const formData = new FormData()
formData.append('file', file)
//
const progressInterval = setInterval(() => {
if (onProgress) {
const percent = Math.min(95, (file.percent || 0) + 10)
file.percent = percent
onProgress({ percent })
}
}, 200)
const { data } = await apis.common.uploadFile(formData)
clearInterval(progressInterval)
const fullUrl = config('http.apiUpload') + data.saved_path
//
if (onProgress) {
onProgress({ percent: 100 })
}
onSuccess({
url: fullUrl
}, file)
emit('uploadSuccess', { url: fullUrl })
} catch (err) {
console.error('上传失败:', err)
onError(err)
emit('uploadError', err)
message.error('上传失败')
} finally {
loading.value = false
}
}
/**
* 触发
* 处理文件变化
*/
function trigger(value) {
emit('update:modelValue', value)
emit('change', value)
const handleChange = ({ file, fileList: updatedList }) => {
console.log('文件状态变化:', file.status)
switch (file.status) {
case 'uploading':
loading.value = true
break
case 'done':
loading.value = false
// URL
const responseUrl = file.response?.url
if (responseUrl) {
const targetFile = updatedList.find(f => f.uid === file.uid)
if (targetFile) {
targetFile.url = responseUrl
}
message.success(`${file.name} 上传成功`)
}
updateModelValue(updatedList)
break
case 'error':
loading.value = false
message.error(`${file.name} 上传失败`)
break
case 'removed':
updateModelValue(updatedList)
break
}
}
/**
* 更新模型值
*/
const updateModelValue = (fileList) => {
const urls = fileList
.filter(item => item.status === 'done' && item.url)
.map(item => item.url)
emit('update:modelValue', urls)
emit('change', urls)
onFieldChange()
}
</script>
<style lang="less" scoped>
.x-upload {
:deep(.ant-input-group-addon) {
padding: 0;
border: 0;
:deep(.ant-upload-wrapper) {
.ant-btn {
display: inline-flex;
align-items: center;
gap: 4px;
}
.ant-btn {
margin: -1px 0 -1px -1px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
.ant-upload-list {
margin-top: 8px;
}
}
</style>

View File

@ -2,6 +2,7 @@ import { env } from '@/utils/util'
export default {
apiBasic: env('apiBasic'),
apiUpload: env('apiUpload'),
code: {
success: true,
},

View File

@ -539,8 +539,8 @@ async function handleEdit(record = {}) {
}
formData.value = { ...data }
formData.value.birthDate = dayjs(data.birthDate)
formData.value.archive.idCardPhotos = data.archive.idCardPhotos ? data.archive.idCardPhotos.map(item => config('http.apiBasic') + item) : []
formData.value.archive.uploadedDocuments = data.archive.uploadedDocuments ? data.archive.uploadedDocuments.map(item => config('http.apiBasic') + item) : []
formData.value.archive.idCardPhotos = data.archive.idCardPhotos ? data.archive.idCardPhotos.map(item => config('http.apiUpload') + item) : []
formData.value.archive.uploadedDocuments = data.archive.uploadedDocuments ? data.archive.uploadedDocuments.map(item => config('http.apiUpload') + item) : []
formData.value.governmentPurchasedServiceStartDate = [dayjs(formData.value.starGovernmentService), dayjs(formData.value.endGovernmentService)]
nextTick(() => {
areaCascaderRef.value.initData(data.archive.homeAreaCodes)

View File

@ -287,7 +287,7 @@ const handleCustomRequest = async (options) => {
const formData = new FormData();
formData.append('file', file);
const { data } = await apis.common.uploadFile(formData);
const fullUrl = config('http.apiBasic') + data;
const fullUrl = config('http.apiUpload') + data;
console.log(fullUrl)
} catch (err) {
message.error('上传失败');

View File

@ -36,6 +36,13 @@
<a-radio value="2"></a-radio>
</a-radio-group>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="状态" name="status">
<a-radio-group v-model:value="formData.status">
<a-radio v-for="item in dicsStore.dictOptions.STAFF_STATUS" :key="item.dval" :value="item.dval">{{ item.introduction }}</a-radio>
</a-radio-group>
</a-form-item>
</a-col>
<!-- 证件号码 -->
@ -170,12 +177,12 @@
</a-col>
<a-col :span="12">
<a-form-item label="证件照片" name="imgs">
<gx-upload v-model="formData.imgs" accept-types=".jpg,.png,.webp" />
<gx-upload v-model="formData.imgs" :fileNumber="1" accept-types=".jpg,.png,.webp" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="附件" name="attachments">
<gx-upload v-model="formData.attachments" accept-types=".jpg,.png,.webp,.xlsx,.docx,.doc" />
<UploadInput v-model="formData.attachments"/>
</a-form-item>
</a-col>
@ -199,6 +206,7 @@ import dayjs from 'dayjs'
import { getBirthDate, spliceUrl } from '@/utils/util'
import { validatePhone, validateEmail, validateIdCard } from '@/utils/validate'
import storage from '@/utils/storage'
import UploadInput from '@/components/Upload/UploadInput.vue'
const emit = defineEmits(['ok'])
const { modal, showModal, hideModal, showLoading, hideLoading } = useModal()
const { formRecord, formData, formRef, formRules, resetForm } = useForm()
@ -219,6 +227,7 @@ formRules.value = {
*/
function handleCreate() {
formData.value.gender = '1'
formData.value.status = '1'
formData.value.companyId = storage.local.getItem('companyId')
formData.value.stationId = storage.local.getItem('stationId')
showModal({
@ -269,12 +278,12 @@ async function handleEdit(record = {}) {
if (formData.value.laborContractStartAt && formData.value.laborContractEndAt) {
formData.value.laborContract = [dayjs(formData.value.laborContractStartAt), dayjs(formData.value.laborContractEndAt)]
}
formData.value.imgs = (data.imgs && data.imgs.length > 0) ? data.imgs.map(item => config('http.apiBasic') + item) : []
formData.value.attachments = (data.attachments && data.attachments.length > 0) ? data.attachments.map(item => config('http.apiBasic') + item) : []
formData.value.imgs = (data.imgs && data.imgs.length > 0) ? data.imgs.map(item => config('http.apiUpload') + item) : []
formData.value.attachments = (data.attachments && data.attachments.length > 0) ? data.attachments.map(item => config('http.apiUpload') + item) : []
formData.value.commissionRate = formData.value.commissionRate ?? formData.value.commissionRate * 100
formData.value.birthDate2 = dayjs(data.birthDate).format('YYYY-DD-MM')
formData.value.birthDate2 = dayjs(data.birthDay).format('YYYY-DD-MM')
nextTick(() => {
areaCascaderRef.value.initData(data.archive.homeAreaCodes)
areaCascaderRef.value.initData(data.sAreaCodes||[])
})
} catch (error) {
@ -294,13 +303,14 @@ function handleOk() {
showLoading()
let params = {
...formData.value,
birthDate: new Date(formData.value.birthDate)
birthDay: formData.value.birthDate2
}
if (formData.value.laborContract && formData.value.laborContract.length > 0) {
params.laborContractStartAt = formData.value.laborContract[0]
params.laborContractEndAt = formData.value.laborContract[1]
}
console.log(formData.value.imgs)
params.imgs = (formData.value.imgs && formData.value.imgs.length) > 0 ? formData.value.imgs.map(item => spliceUrl(item)) : []
params.attachments = (formData.value.attachments && formData.value.attachments.length) > 0 ? formData.value.attachments.map(item => spliceUrl(item)) : []
params.commissionRate = formData.value.commissionRate ?? formData.value.commissionRate / 100

View File

@ -4,74 +4,90 @@
<a-card class="">
<div style="display: flex;justify-content: space-around;">
<div
style="width:200px;margin-top: 20px;border-right: 1px solid #f0f0f0;display: flex;flex-direction: column;align-items: center;">
style="width:250px;margin-top: 20px;border-right: 1px solid #f0f0f0;display: flex;flex-direction: column;align-items: center;">
<gx-upload v-model="formData.imgList" accept-types=".jpg,.png,.webp" :fileNumber="1" />
<div>
<p>姓名{{ formData.name }}</p>
<p>性别{{ formData.gender==1?'男':'女' }}</p>
<p>身份证号{{ formData.idNumber }}</p>
<p>手机号{{ formData.contact1 }}</p>
<p>性别{{ formData.gender == 1 ? '男' : '女' }}</p>
<p>身份证号{{ formData.idCard }}</p>
<p>手机号{{ formData.phone }}</p>
<p>状态 <a-tag>{{ dicsStore.getDictLabel('STAFF_STATUS', formData.status) }}</a-tag></p>
</div>
</div>
<div style="width: calc(100% - 200px);padding: 20px;">
<a-row :gutter="20">
<a-col :span="8">
<div><span class="label">出生日期:</span> {{ formData.birthDate || '-' }}</div>
<div><span class="label">出生日期:</span> {{ formData.birthday || '-' }}</div>
</a-col>
<!-- 基本信息 -->
<a-col :span="8">
<div><span class="label">参保情况:</span> {{ formData.socialSecurityCardNumber || '-' }}</div>
<div><span class="label">参保情况:</span> {{ dicsStore.getDictLabel('INSURANCE_STATUS',
formData.insuranceStatus) || '-' }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">护理人员类型:</span> {{ dicsStore.getDictLabel('STAFF_TYPE',formData.serviceType)}}</div>
<div><span class="label">学历:</span> {{ dicsStore.getDictLabel('Level_Education',
formData.education) || '-' }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">护理人员类型:</span> {{
dicsStore.getDictLabel('STAFF_TYPE', formData.serviceType) }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">用工形式:</span> {{ formData.idNumber || '-' }}</div>
<div><span class="label">用工形式:</span> {{ dicsStore.getDictLabel('USE_TYPE',
formData.workType) || '-' }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">提成比例:</span> {{ formData.commissionRate + '%' || '-' }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">负责区域:</span> {{ formData.nursingLevel || '-' }}</div>
<div><span class="label">入职日期:</span> {{ dayjs(formData.joinAt).format('YYYY-MM-DD') || '-'
}}</div>
</a-col>
<a-col :span="8">
<div><span class="label">提成比例:</span> {{ formData.healthStatus || '-' }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">入职日期:</span> {{ formData.survivalStatus || '-' }}</div>
<div><span class="label">资格证照名称:</span> {{ formData.qualificationName || '-' }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">资格证照名称:</span> {{ formData.serviceStatus || '-' }}</div>
<div><span class="label">资格证照等级:</span> {{ dicsStore.getDictLabel('QUA_LV',
formData.qualificationLV) || '-' }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">资格证照等级:</span> {{ formData.serviceForm || '-' }}</div>
<div><span class="label">银行卡号:</span> {{ formData.bankNo || '-' }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">银行卡号:</span> {{ formData.serviceForm || '-' }}</div>
<div><span class="label">开户行名称:</span> {{ formData.bankName || '-' }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">开户行名称:</span> {{ formData.serviceForm || '-' }}</div>
<div><span class="label">补贴类型:</span> {{ dicsStore.getDictLabel('SUBSIDY_TYPE',
formData.subsidyType) || '-' }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">家庭住址:</span> {{ formData.serviceForm || '-' }}</div>
<a-col :span="24">
<div><span class="label">负责区域:</span> {{ formData.sAreaLabels ?
formData.sAreaLabels.join('/')
: '-' }}</div>
</a-col>
<!-- 数组类字段 -->
<a-col :span="24" v-if="formData.idCardPhotos && formData.idCardPhotos.length > 0">
<a-col :span="24">
<div><span class="label">家庭住址:</span> {{ formData.areaLabels ? formData.areaLabels.join('/')
:
'-' }}</div>
</a-col>
<a-col :span="24">
<div><span class="label">备注:</span> {{ formData.remark || '-' }}</div>
</a-col>
<a-col :span="24">
<div>
<span class="label">证件照:</span>
<div style="margin-top: 8px;">
<a-image v-for="(url, index) in formData.idCardPhotos" :key="index" :src="url"
fit="cover" style="width: 100px; height: 60px; margin-right: 8px;"
:preview-src-list="formData.idCardPhotos" />
<span class="label">附件:</span>
<div>
<a v-for="value in formData.attachments" :key="value" :href="value"
target="_blank">{{ value }}</a>
</div>
</div>
</a-col>
<a-col :span="8">
<div><span class="label">附件:</span> {{ formData.serviceForm || '-' }}</div>
</a-col>
<a-col :span="8">
<div><span class="label">备注:</span> {{ formData.serviceForm || '-' }}</div>
</a-col>
</a-row>
</div>
</div>
@ -86,7 +102,7 @@ import { ref, computed, defineAsyncComponent, defineExpose, getCurrentInstance,
import { config } from '@/config'
import apis from '@/apis'
import { useForm, useModal } from '@/hooks'
import dayjs from 'dayjs'
import { useDicsStore } from '@/store'
const emit = defineEmits(['ok'])
const activeKey = ref('1')
@ -111,14 +127,19 @@ function handleCreate() {
/**
* 编辑
*/
function handleEdit(record = {}) {
console.log('record', record)
async function handleEdit(record = {}) {
showModal({
type: 'edit',
title: '服务人员详情',
})
formRecord.value = record
formData.value = cloneDeep(record)
const { data, success } = await apis.serviceStaffList.getItem(record.id).catch()
if (!success) {
hideModal()
return
}
formData.value = data
formData.value.imgList = data.imgs ? data.imgs.map(item => config('http.apiUpload') + item) : []
formData.value.attachments = (data.attachments && data.attachments.length > 0) ? data.attachments.map(item => config('http.apiUpload') + item) : []
}
const callback = (val) => {
console.log(val);
@ -173,7 +194,8 @@ function handleCancel() {
* 关闭后
*/
function onAfterClose() {
resetForm()
// resetForm()
formData.value = {}
hideLoading()
}

View File

@ -77,11 +77,18 @@
<template #bodyCell="{ index, column, record }">
<template v-if="column.key === 'serialNumber'">
<span>{{ index + 1 }}</span>
</template>
<template v-if="column.key === 'workType'">
<span>{{ dicsStore.getDictLabel('USE_TYPE', record.workType) }}</span>
</template>
<template v-if="column.key === 'gender'">
<span v-if="record.gender == '1'"></span>
<span v-else></span>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status == '1'" color="green">启用</a-tag>
<a-tag v-else color="red">禁用</a-tag>
</template>
<template v-if="column.key === 'serviceType'">
<span>{{ dicsStore.getDictLabel('STAFF_TYPE', record.serviceType) }}</span>
</template>
@ -208,7 +215,7 @@ const columns = [
dataIndex: 'action',
key: 'action',
align: 'center',
width: 320,
width: 180,
fixed: 'right',
}
];
@ -217,66 +224,6 @@ const { listData, loading, showLoading, hideLoading, paginationState, resetPagin
const editDialogRef = ref()
const detailRef = ref()
const treeData = ref([
{
title: 'Node1',
value: '0-0',
key: '0-0',
children: [
{
value: '0-0-1',
key: '0-0-1',
slots: {
title: 'title',
},
},
{
title: 'Child Node2',
value: '0-0-2',
key: '0-0-2',
},
],
},
{
title: 'Node2',
value: '0-1',
key: '0-1',
},
]);
const options = ref([
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},])
getPageList()
async function getPageList() {

View File

@ -529,8 +529,8 @@ async function handleEdit(record = {}) {
}
formData.value = { ...data }
formData.value.birthDate = dayjs(data.birthDate)
formData.value.archive.idCardPhotos = data.archive.idCardPhotos ? data.archive.idCardPhotos.map(item => config('http.apiBasic') + item) : []
formData.value.archive.uploadedDocuments = data.archive.uploadedDocuments ? data.archive.uploadedDocuments.map(item => config('http.apiBasic') + item) : []
formData.value.archive.idCardPhotos = data.archive.idCardPhotos ? data.archive.idCardPhotos.map(item => config('http.apiUpload') + item) : []
formData.value.archive.uploadedDocuments = data.archive.uploadedDocuments ? data.archive.uploadedDocuments.map(item => config('http.apiUpload') + item) : []
formData.value.governmentPurchasedServiceStartDate = [dayjs(formData.value.starGovernmentService), dayjs(formData.value.endGovernmentService)]
nextTick(() => {
areaCascaderRef.value.initData(data.archive.homeAreaCodes)