<template> <cs-page> <view class="detail-container" :style="{ paddingBottom: isSelect.length > 0 ? '70px' : '20px' }" > <view class="box detail"> <text class="wd-font-800 wd-text-16" style="margin-bottom: 4px"> {{ detail.title }} </text> <view class="tagList"> <view v-for="(tag, index) in detail.tagList" :key="index" class="tag" > {{ tag }} </view> </view> <view class="wd-flex wd-text-13" style="justify-content: space-between" > <view class="wd-flex" style="align-items: center; gap: 4px"> <u-icon name="calendar" color="#17C653" /> <text class="wd-text-13 wd-ml-4px"> {{ `${$util.formatDate( detail.startDate, 'YYYY/M/D' )}~${$util.formatDate(detail.endDate, 'YYYY/M/D')}` }} </text> </view> </view> <view style=" font-size: 13px; line-height: 160%; position: relative; transform: 0.2s all; " v-if="detail.description.length < 20" > {{ detail.description }} </view> <view v-else style=" font-size: 13px; line-height: 160%; position: relative; transform: 0.2s all; " @tap="isShowAllText = !isShowAllText" > {{ isShowAllText ? detail.description : `${detail.description.slice(1, 20)}...` }} <text style="color: #17c653; padding-left: 4px"> {{ isShowAllText ? '收起' : '展开' }} </text> </view> <view class="audit"> <cs-dict-tag :dict="dictMap.task_state" :value="detail.status" ></cs-dict-tag> </view> </view> <u-sticky> <view :class="[ 'options-container', OptionsOffset.isTop ? 'isTop' : '' ]" ref="optionRef" id="options" > <van-dropdown-menu safe-area-tab-bar active-color="#17C653" style="flex: 1" > <van-dropdown-item v-if="showItem(['director'])" :value="queryParams.departmentId" :options="getDropdownOption('dept')" @change=" v => { querySelect(v, 'departmentId') } " /> <van-dropdown-item v-if="showItem(['queue', 'director'])" :value="queryParams.userId" use-before-toggle @before-toggle="getUserOption" :options="userList" @change=" v => { querySelect(v, 'userId') } " /> <van-dropdown-item :value="queryParams.Inspections_status" :options="getDropdownOption('Inspections_status')" @change=" v => { querySelect(v, 'Inspections_status') } " /> </van-dropdown-menu> <view class="wd-font-600"> (当前 <text style="color: #17c653">{{ list.length }}</text> 条记录) </view> </view> </u-sticky> <view class="record" v-for="record in list" :key="record.id" @tap="goRecord(record)" > <view :class="{ select: true, isSelect: isSelect.includes(record.id) }" @tap.native.stop="select(record.id)" v-if="['3', null].includes(record.inspectionStatus)" > <u-icon name="checkbox-mark" size="16px" color="#fff"></u-icon> </view> <view class="disable" v-else></view> <view class="info"> <view class="name">{{ record.enterpriseName }}</view> <view style="font-size: 1 3px"> {{ record.enterpriseAddress }} </view> <view class="tagList" style="margin-top: 8px"> <view v-for="(item, index) in record.tagList" :key="index" class="tag" > {{ item }} </view> </view> </view> <view class="audit" v-show="record.inspectionStatus"> <cs-dict-tag :dict="dictMap.Inspections_status" :value="record.inspectionStatus" ></cs-dict-tag> </view> </view> <u-loadmore :status="load" marginTop="12" marginBottom="12" /> <view class="btn-box" v-if="isSelect.length > 0"> <view class="confirm-btn" @tap="replay"> 已选择({{ isSelect.length }}) </view> </view> <u-modal :show="model.show" closeOnClickOverlay @close="closeModel"> <view class="wd-flex wd-flex-col" style="gap: 20px"> <view class="header"> <view class="row-1"> 已选择 <text class="num">{{ isSelect.length }}项记录</text> 转移给 </view> <view class="row-2">每次只能选择一人</view> </view> <view class="staff-list"> <view class="staff" v-for="staff in model.list" :key="staff.userId" :style="{ '--select-color': model.isSelect == staff.userId ? '#17c653' : '#f1f1f4' }" @click="selectUser(staff.userId)" > <u-avatar size="40" shape="circle" :src="staff.avatar" ></u-avatar> <view class="info"> <view class="name">{{ staff.realName }}</view> <view class="dept">{{ staff.roleName[0] }}</view> </view> <view class="isSelect"> <u-icon name="checkbox-mark" size="16px" color="#fff" ></u-icon> </view> </view> </view> </view> <template #confirmButton> <view class="wd-flex wd-flex-center"> <view class="confirm-btn" @tap="submitReplay">转移给TA</view> </view> </template> </u-modal> </view> </cs-page> </template> <script> import { getDeptTree, getDictBatchByType } from '@/api/system/dict.js' import { TaskApi } from '@/api/task/index.js' import { InspectionsApi } from '@/api/inspections/index.js' import { getUserList } from '@/api/system/user.js' export default { data() { return { // 任务详情 detail: { id: '', description: '' }, list: [], // 是否展开文本 isShowAllText: false, // 已选中 isSelect: [], dictMap: {}, //u-sticky 样式开关 OptionsOffset: { top: 0, isTop: false }, // 查询条件 queryParams: { departmentId: '', Inspections_status: '', pageSize: 8, pageNo: 1, userId: '' }, load: 'loadmore', model: { show: false, list: [], isSelect: '' }, userList: [ { value: '', text: '按人员' } ] } }, onLoad(res) { this.detail.id = res.id this.getDict() }, onPageScroll(e) { this.OptionsOffset.isTop = e.scrollTop + 10 > this.OptionsOffset.top }, onReady() { this.getOptionOffset() }, onReachBottom() { this.loadMore() }, onPullDownRefresh() { this.reset() }, onShow() { this.init() this.reset() }, methods: { getOptionOffset() { const query = uni.createSelectorQuery().in(this) query .select('#options') .boundingClientRect(data => { this.OptionsOffset.top = data.top }) .exec() }, showItem(query) { return this.$roles.checkRole(query) }, /** * 获取字典 */ async getDict() { const dict = await getDictBatchByType({ type: ['task_state', 'Inspections_status'].join(',') }) const dept = await getDeptTree() this.dictMap = { ...dict.data, dept: dept.data } }, getDropdownOption(key) { const keyMap = { dept: '按部门', Inspections_status: '按状态' } if (!this.dictMap[key]) return [] if (key == 'dept') { return [ ...this.dictMap[key].map(d => { return { ...d, value: d.id, text: d.name } }), { value: '', text: keyMap[key] } ] } return [ ...this.dictMap[key].map(d => { return { value: d.value, text: d.label } }), { value: '', text: keyMap[key] } ] }, querySelect(v, key) { if (key == 'departmentId') { this.queryParams.userId = '' } this.queryParams[key] = v.detail this.queryList() }, async init() { const res = await TaskApi.getDetail(this.detail.id) this.detail = res.data }, async getUser() { const res = await getUserList(this.queryParams.departmentId) return res.data }, async getList() { if (!this.$roles.checkRole(['director'])) { this.queryParams.departmentId = this.$store.getters.deptId } uni.showToast({ title: '加载中', mask: true, icon: 'loading' }) this.load = 'loading' const { data } = await InspectionsApi.getList({ ...this.queryParams, taskId: this.detail.id }) this.list.push(...data.list) this.load = 'loadmore' if (this.list.length == data.total) { this.load = 'nomore' } uni.hideToast() }, getUserOption({ detail }) { this.getUser().then(res => { this.userList = [ ...res.map(i => { return { ...i, value: i.userId, text: i.realName } }), { value: '', text: '按人员' } ] detail.callback(true) }) }, goBack() { uni.switchTab({ url: '/pages/task' }) }, loadMore() { if (this.load == 'nomore') { uni.showToast({ title: '没有更多了', icon: 'none' }) return } this.queryParams.pageNo++ this.getList() }, select(id) { if (this.isSelect.includes(id)) { this.isSelect = this.isSelect.filter(i => i != id) } else { this.isSelect.push(id) } }, async reset() { this.queryParams = { departmentId: '', Inspections_status: '', pageSize: 8, pageNo: 1, userId: '' } await this.queryList() }, queryList() { this.queryParams.pageNo = 1 this.load = 'loadmore' this.list = [] this.getList() }, replay() { this.getUser().then(res => { this.model.list = res this.model.show = true }) }, closeModel() { this.model = { show: false, list: [], isSelect: '' } }, selectUser(id) { if (this.model.isSelect == id) { this.model.isSelect = null } else { this.model.isSelect = id } }, goRecord(record) { uni.navigateTo({ url: `/sub/task/record?taskId=${record.taskId}&recordId=${record.id}&enterpriseId=${record.enterpriseId}` }) }, submitReplay() { InspectionsApi.replay({ inspectionsId: this.isSelect, userId: this.model.isSelect }).then(res => { uni.showToast({ icon: 'none', title: '任务转移成功' }) this.closeModel() this.reset() }) } } } </script> <style lang="scss" scoped> .detail-container { position: relative; color: $cs-color-grey; .box { margin: 12px; background-color: #fff; border-radius: $cs-border-radius; padding: 16px; } .detail { display: flex; flex-flow: column nowrap; gap: 12px; position: relative; overflow: hidden; .audit { position: absolute; right: 0; top: 0; transform: translateX(23px) translateY(16px) rotateZ(45deg); transform-origin: 50% 50%; } } .tagList { display: flex; align-items: center; gap: var(--Number-4px, 4px); .tag { display: flex; padding: var(--Number-2px, 2px) var(--Number-6px, 6px); justify-content: center; align-items: center; gap: var(--Number-4px, 4px); border-radius: var(--Number-2px, 2px); background: var(--LightMode-Grey-Grey-100, #f9f9f9); color: var(--LightMode-Grey-Grey-600, #78829d); text-align: center; font-size: 12px; font-style: normal; font-weight: 400; line-height: normal; } } .record { background-color: #fff; display: flex; padding: 12px 16px; align-items: center; border-radius: var(--Number-8px, 8px); border: 1px solid var(--LightMode-Grey-Grey-100, #f9f9f9); margin: 12px; margin-top: 0; position: relative; overflow: hidden; .audit { position: absolute; right: 0; top: 0; transform-origin: 50% 50%; transform: translateX(30px) translateY(10px) rotateZ(45deg); } .select { width: 24px; height: 24px; border: 2px solid $cs-color-main; border-radius: 6px; display: flex; align-items: center; justify-content: center; transition: 0.2s all; margin-right: 16px; } .disable { width: 24px; height: 24px; border: 2px solid #dbdfe9; margin-right: 16px; border-radius: 100%; position: relative; &::after { content: ''; position: absolute; inset: 0; border-left: 2px solid #dbdfe9; transform-origin: 0 50%; transform: translateX(calc(50% - 1px)) rotateZ(45deg); } } .isSelect { background-color: $cs-color-main; } .info { font-size: 13px; .name { font-size: 16px; font-weight: 600; color: #071437; margin-bottom: 4px; } } &:active { background-color: $cs-color-touch; } } .btn-box { width: 100%; display: flex; align-items: center; justify-content: center; padding-bottom: 30px; position: fixed; bottom: 0; left: 0; } .confirm-btn { background-color: $cs-color-main; padding: 12px 40px; color: #fff; font-size: 16px; font-weight: 600; border-radius: 120px; } .header { display: flex; flex-flow: column nowrap; justify-content: center; align-items: center; gap: 4px; .row-1 { font-size: 16px; font-weight: 600; color: #071437; .num { color: $cs-color-main; padding: 0 4px; } } .row-2 { color: $cs-color-grey; } } .staff-list { width: 70vw; display: flex; flex-flow: row wrap; gap: 12px; max-height: 30vh; overflow-y: scroll; .staff { width: calc(50% - 6px); padding: 12px; border-radius: 6px; border: 1px solid var(--select-color, #dbdfe9); position: relative; gap: 8px; display: flex; .info { .name { font-size: 16px; font-weight: bold; } .dept { color: $cs-color-grey; } } .isSelect { position: absolute; right: 0; top: 0; width: 30px; height: 30px; background-image: linear-gradient( 45deg, rgba(255, 255, 255, 0) 50%, var(--select-color, #f1f1f4) 50% ); display: flex; justify-content: flex-end; align-items: flex-start; } } } } .options-container { overflow: hidden; margin: 12px; border-radius: 8px; transition: 0.1s all; display: flex; padding: 0 12px; flex-flow: row nowrap; justify-content: center; align-items: center; background-color: #fff; border-bottom: 1px solid #f1f1f4; color: #071437; ::v-deep .van-dropdown-menu { background-color: transparent; box-shadow: none; --dropdown-menu-title-font-size: 13px; --dropdown-menu-title-text-color: #071437; .van-dropdown-menu__item { flex: 0; .van-dropdown-menu__title { width: 80px; } } } } .isTop { margin: 12px 0; border-radius: 0px; } </style>