| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- <template>
- <view class="common-page" style="padding: 0;">
- <view class="form-page">
- <u-form ref="formRef" :model="form" :rules="rules" labelPosition="left" labelWidth="85px">
- <u-form-item label="快递单号:" prop="waybillCode" required>
- <template v-if="isReadonly && form.waybillCode">
- <text class="readonly-text">{{ form.waybillCode }}</text>
- </template>
- <u-input v-else v-model="form.waybillCode" placeholder="请输入快递单号" clearable></u-input>
- </u-form-item>
- <!-- <u-form-item label="订单编号:" prop="orderId" v-if="form.orderId || isReadonly">
- <template v-if="isReadonly && form.orderId">
- <text class="clickable-text" @click="goToOrderDetail">{{ form.orderId }}</text>
- </template>
- <u-input v-else v-model="form.orderId" placeholder="请输入订单编号" clearable></u-input>
- </u-form-item> -->
- <u-form-item label="承运商:" prop="carrier" required>
- <u-input v-model="carrierText" readonly suffixIcon="arrow-down" placeholder="请选择"
- @click="!isReadonly && (showCarrierPicker = true)"></u-input>
- </u-form-item>
- <u-form-item label="验货状态:" prop="verifyStatus" required>
- <u-input v-model="verifyText" readonly suffixIcon="arrow-down" placeholder="请选择"
- @click="showVerifyPicker = true"></u-input>
- </u-form-item>
- <u-form-item label="任务类型:" prop="taskType" required>
- <u-input v-model="taskTypeText" readonly suffixIcon="arrow-down" placeholder="请选择"
- @click="showTaskTypePicker = true"></u-input>
- </u-form-item>
- <u-form-item label="任务详情:" prop="taskDetail" required>
- <u-textarea v-model="form.taskDetail" placeholder="请输入" :height="120"
- :autoHeight="true"></u-textarea>
- </u-form-item>
- <u-form-item label="上传图片:" prop="images" required>
- <cy-upload :filename="form.images" @update:filename="form.images = $event" />
- </u-form-item>
- <view class="divider"></view>
- <u-form-item label="指派给:" prop="assignTo">
- <u-input v-model="assignText" readonly suffixIcon="arrow-down" placeholder="请选择 (非必填)"
- @click="openAssignPicker"></u-input>
- </u-form-item>
- </u-form>
- </view>
- <u-picker :show="showCarrierPicker" :columns="[carrierOptions]" title="选择承运商" @confirm="onPickCarrier"
- @cancel="showCarrierPicker = false" @close="showCarrierPicker = false"></u-picker>
- <u-picker :show="showVerifyPicker" :columns="[verifyOptions]" title="选择验货状态" @confirm="onPickVerify"
- @cancel="showVerifyPicker = false" @close="showVerifyPicker = false"></u-picker>
- <u-picker :show="showTaskTypePicker" :columns="[taskTypeOptions]" title="选择任务类型" @confirm="onPickTaskType"
- @cancel="showTaskTypePicker = false" @close="showTaskTypePicker = false"></u-picker>
- <!-- 指派给选择器 - 使用 popup + checkbox 实现多选 -->
- <u-popup v-model:show="showAssignPicker" mode="bottom" :round="20">
- <view class="assign-picker-content">
- <view class="assign-header">
- <text class="title">选择指派人</text>
- <view class="actions">
- <text class="cancel-btn" @click="showAssignPicker = false">取消</text>
- <text class="confirm-btn" @click="confirmAssign">确定</text>
- </view>
- </view>
- <view class="assign-body">
- <view
- v-for="(item, index) in assignOptions"
- :key="index"
- class="assign-item"
- @click="toggleAssign(index)"
- >
- <text class="item-text">{{ item.text }}</text>
- <view
- class="custom-checkbox"
- :class="{ checked: tempAssignIndexes.includes(index) }"
- >
- </view>
- </view>
- </view>
- </view>
- </u-popup>
- <view class="fixed-bottom">
- <u-button type="warning" size="large" @click="onCancel">取消</u-button>
- <u-button type="primary" size="large" :loading="submitting" @click="onSubmit">提交</u-button>
- </view>
- </view>
- </template>
- <script setup>
- import { ref } from 'vue'
- import { onLoad } from '@dcloudio/uni-app'
- import CyUpload from '@/components/cy-upload/index.vue'
- const formRef = ref(null)
- const submitting = ref(false)
- const form = ref({
- waybillCode: '',
- orderId: '',
- expressType: '',
- verifyStatus: '',
- taskType: '',
- taskDetail: '',
- images: [],
- assignTo: []
- })
- const rules = {
- waybillCode: [{ required: true, message: '请输入快递单号' }],
- expressType: [{ required: true, message: '请选择承运商' }],
- verifyStatus: [{ required: true, message: '请选择验货状态' }],
- taskType: [{ required: true, message: '请选择任务类型' }],
- taskDetail: [{ required: true, message: '请输入任务详情' }],
- assignTo: [{ required: false, message: '请选择指派对象' }]
- }
- const carrierOptions = ref([])
- const verifyOptions = [
- { text: '已验货', value: 1 },
- { text: '未验货', value: 0 }
- ]
- const taskTypeOptions = ref([])
- const assignOptions = ref([{ text: '默认(不指派)', value: '' }])
- // 获取快递公司字典
- const getCarrierOptions = async () => {
- try {
- const res = await uni.$u.http.get('/system/dict/data/type/shop_order_express_name')
- if (res.code === 200 && res.data) {
- carrierOptions.value = res.data.map(item => ({
- text: item.dictLabel,
- value: item.dictValue
- }))
- }
- } catch (e) {
- console.error('获取快递公司失败', e)
- }
- }
- // 获取任务类型字典
- const getTaskTypeOptions = async () => {
- try {
- const res = await uni.$u.http.get('/system/dict/data/type/sell_task_type')
- if (res.code === 200 && res.data) {
- taskTypeOptions.value = res.data.map(item => ({
- text: item.dictLabel,
- value: parseInt(item.dictValue)
- }))
- }
- } catch (e) {
- console.error('获取任务类型失败', e)
- }
- }
- // 获取指派人列表
- const getAssignOptions = async () => {
- try {
- const res = await uni.$u.http.get('/system/user/list', {
- params: {
- pageSize: 1000
- }
- })
- if (res.code === 200 && res.rows) {
- assignOptions.value = res.rows.map(item => ({
- text: item.nickName,
- value: item.userId
- }))
- }
- } catch (e) {
- console.error('获取指派人失败', e)
- }
- }
- const showCarrierPicker = ref(false)
- const showVerifyPicker = ref(false)
- const showTaskTypePicker = ref(false)
- const showAssignPicker = ref(false)
- // 临时存储选中的索引数组
- const tempAssignIndexes = ref([])
- const carrierText = ref('')
- const verifyText = ref('')
- const taskTypeText = ref('')
- const assignText = ref('')
- const onPickCarrier = (e) => {
- const cell = e.value?.[0]
- form.value.expressType = cell?.value || ''
- carrierText.value = cell?.text || ''
- showCarrierPicker.value = false
- console.log('选择承运商:', form.value.expressType, carrierText.value)
- }
- const onPickVerify = (e) => {
- const cell = e.value?.[0]
- form.value.verifyStatus = cell?.value || ''
- verifyText.value = cell?.text || ''
- showVerifyPicker.value = false
- console.log('选择验货状态:', form.value.verifyStatus, verifyText.value)
- }
- const onPickTaskType = (e) => {
- const cell = e.value?.[0]
- form.value.taskType = cell?.value || ''
- taskTypeText.value = cell?.text || ''
- showTaskTypePicker.value = false
- console.log('选择任务类型:', form.value.taskType, taskTypeText.value)
- }
- // 打开指派人选择器时,初始化临时选中状态
- const openAssignPicker = () => {
- if (isReadonly.value) return
- // 根据当前已选择的值,初始化 tempAssignIndexes
- tempAssignIndexes.value = []
- if (form.value.assignTo && form.value.assignTo.length > 0) {
- assignOptions.value.forEach((item, index) => {
- if (form.value.assignTo.includes(item.value)) {
- tempAssignIndexes.value.push(index)
- }
- })
- }
- showAssignPicker.value = true
- console.log('打开选择器,已选中索引:', tempAssignIndexes.value)
- }
- const onPickAssign = (e) => {
- // 这个方法不再使用,改用 toggleAssign 和 confirmAssign
- }
- // 切换选中状态
- const toggleAssign = (index) => {
- const idx = tempAssignIndexes.value.indexOf(index)
- if (idx > -1) {
- tempAssignIndexes.value.splice(idx, 1)
- } else {
- tempAssignIndexes.value.push(index)
- }
- }
- // 确认选择
- const confirmAssign = () => {
- const selectedList = []
- tempAssignIndexes.value.forEach(index => {
- selectedList.push(assignOptions.value[index])
- })
-
- form.value.assignTo = selectedList.map(item => item.value)
- assignText.value = selectedList.map(item => item.text).join(',')
- showAssignPicker.value = false
- console.log('选择指派人:', form.value.assignTo, assignText.value)
- }
- const onCancel = () => {
- uni.navigateBack()
- }
- const goToOrderDetail = () => {
- if (!form.value.orderId) return
- uni.navigateTo({
- url: `/pages/index/detail/index?id=${form.value.orderId}`
- })
- }
- const workOrderId = ref('')
- const isEdit = ref(false)
- const isReadonly = ref(false)
- const onLoadDetail = async (id) => {
- try {
- const res = await uni.$u.http.get('/app/workOrder/getWorkOrderDetail', {
- params: { workOrderId: id }
- })
- if (res.code === 200 && res.data) {
- const data = res.data
- form.value.waybillCode = data.waybillCode || ''
- form.value.orderId = data.orderId || ''
- form.value.taskDetail = data.deatil || data.taskDetail || ''
- form.value.images = data.imgInfo?.imgUrlList.length ? data.imgInfo.imgUrlList : []
-
- // 加载任务类型字典
- await getTaskTypeOptions()
- // 回显任务类型
- if (data.taskType) {
- form.value.taskType = data.taskType
- const matchedType = taskTypeOptions.value.find(item => item.value === data.taskType)
- if (matchedType) {
- taskTypeText.value = matchedType.text
- }
- }
- // 回显指派人(多选)
- if (data.handleUsers && data.handleUsers.length > 0) {
- form.value.assignTo = data.handleUsers.map(user => user.userId)
- assignText.value = data.handleUsers.map(user => user.userName).join(',')
- }
- if (data.inspectionStatus !== undefined && data.inspectionStatus !== null) {
- form.value.verifyStatus = data.inspectionStatus
- const matchedVerify = verifyOptions.find(item => item.value === data.inspectionStatus)
- if (matchedVerify) {
- verifyText.value = matchedVerify.text
- }
- }
- // 回显承运商
- if (data.expressType) {
- form.value.expressType = data.expressType
- // 从字典中查找对应的文本
- const matchedCarrier = carrierOptions.value.find(item => {
- return item.value === data.expressType
- })
- if (matchedCarrier) {
- carrierText.value = matchedCarrier.text
- }
- }
- }
- } catch (e) {
- console.error(e)
- }
- }
- const onSubmit = async () => {
- if (submitting.value) return
- try {
- await formRef.value?.validate?.()
- } catch (e) {
- return
- }
- submitting.value = true
- const payload = {
- taskType: form.value.taskType || 9,
- inspectionStatus: form.value.verifyStatus ?? 0,
- deatil: form.value.taskDetail,
- expressType: form.value.expressType || 0,
- waybillCode: form.value.waybillCode,
- orderId: form.value.orderId || '',
- type: 1, // 工单类型:1-卖书
- imgInfo: form.value.images.length > 0 ? form.value.images : [],
- handleUsers: form.value.assignTo.length > 0 ? form.value.assignTo : []
- }
- if (isEdit.value) {
- payload.id = workOrderId.value
- payload.updateType = 1
- }
- const apiUrl = isEdit.value ? '/app/workOrder/update' : '/app/workOrder/createWorkOrder'
- try {
- const res = await uni.$u.http.post(apiUrl, payload)
- if (res?.code === 200) {
- uni.$u.toast('提交成功')
- uni.navigateBack()
- } else {
- uni.$u.toast(res?.msg || '提交失败')
- }
- } catch (err) {
- uni.$u.toast('网络错误,已本地保存')
- } finally {
- submitting.value = false
- }
- }
- onLoad((options) => {
- form.value.waybillCode = options?.waybillCode || ''
- form.value.orderId = options?.orderId || ''
- if (options?.readonly == 1) {
- isReadonly.value = true
- // 尝试回显承运商
- if (options?.expressType !== undefined && options?.expressType !== null) {
- form.value.expressType = parseInt(options.expressType)
- // 从字典中查找对应的文本
- const matchedCarrier = carrierOptions.value.find(item => item.value === options.expressType)
- if (matchedCarrier) {
- carrierText.value = matchedCarrier.text
- }
- }
- }
- if (options?.mode === 'edit' && options?.workOrderId) {
- isEdit.value = true
- isReadonly.value = true
- workOrderId.value = options.workOrderId
- uni.setNavigationBarTitle({ title: '编辑工单' })
- onLoadDetail(options.workOrderId)
- } else {
- uni.setNavigationBarTitle({ title: '提交工单' })
- }
- // 加载字典数据
- getCarrierOptions()
- getTaskTypeOptions()
- getAssignOptions()
- })
- </script>
- <style lang="scss" scoped>
- .form-page {
- padding: 24rpx 24rpx 140rpx;
- background: #ffffff;
- }
- .divider {
- height: 16rpx;
- background: #f4f5f5;
- margin: 20rpx 0;
- }
- // 指派人多选选择器样式
- .assign-picker-content {
- padding: 0 24rpx;
- background: #fff;
- border-radius: 20rpx 20rpx 0 0;
-
- .assign-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 24rpx 0;
- border-bottom: 1px solid #eee;
-
- .title {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
-
- .actions {
- display: flex;
- gap: 24rpx;
-
- .cancel-btn {
- font-size: 28rpx;
- color: #666;
- padding: 8rpx 16rpx;
- }
-
- .confirm-btn {
- font-size: 28rpx;
- color: #2979ff;
- font-weight: 500;
- padding: 8rpx 16rpx;
- }
- }
- }
-
- .assign-body {
- max-height: 600rpx;
- overflow-y: auto;
- padding: 16rpx 0;
-
- .assign-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 24rpx 16rpx;
- border-bottom: 1px solid #f5f5f5;
-
- &:active {
- background: #f8f9fa;
- }
-
- .item-text {
- font-size: 30rpx;
- color: #333;
- }
-
- .custom-checkbox {
- width: 40rpx;
- height: 40rpx;
- border: 2px solid #c8c9cc;
- border-radius: 8rpx;
- position: relative;
- transition: all 0.2s;
-
- &.checked {
- background: #2979ff;
- border-color: #2979ff;
-
- &::after {
- content: '✓';
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- color: #fff;
- font-size: 28rpx;
- font-weight: bold;
- }
- }
- }
- }
- }
- }
- .readonly-text {
- font-size: 28rpx;
- color: #333;
- line-height: 60rpx;
- }
- .clickable-text {
- font-size: 28rpx;
- color: #2979ff;
- line-height: 60rpx;
- }
- </style>
|