feat:页面开发
This commit is contained in:
parent
a7d3eeae3a
commit
6903fe2d02
20
App.vue
20
App.vue
@ -154,6 +154,26 @@ uni-button[type="primary"]:not([disabled]):active {
|
||||
background: rgb(248 113 113);
|
||||
}
|
||||
|
||||
.bg-light-text-color {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bg-light-text-color::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.2;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.py-3 {
|
||||
padding-top: 6rpx;
|
||||
padding-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.py-5 {
|
||||
padding-top: 10rpx;
|
||||
padding-bottom: 10rpx;
|
||||
|
||||
@ -36,4 +36,4 @@ export const statusClassNames = {
|
||||
processing: "text-danger",
|
||||
cancelled: "text-gray",
|
||||
expired: "text-gray",
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,6 +258,12 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "联系企微客服"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/work/todo/todo-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "待办详情"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
313
pages/work/todo/todo-detail.vue
Normal file
313
pages/work/todo/todo-detail.vue
Normal file
@ -0,0 +1,313 @@
|
||||
<template>
|
||||
<full-page v-if="todo" pageClass="bg-white">
|
||||
<view class="px-15 py-12 border-b">
|
||||
<view class="flex items-center">
|
||||
<view class="mr-5 text-lg text-dark font-semibold">{{ todo.eventTypeLabel }}</view>
|
||||
<view class="bg-light-text-color px-10 py-3 leading-normal text-base rounded overflow-hidden"
|
||||
:class="statusClassNames[todo.eventStatus] || 'text-gray'">
|
||||
{{ todo.eventStatusLabel }}
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!canEdit" class="mt-5 text-base text-gray break-all">
|
||||
{{ todo.taskContent || "暂无任务内容" }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="px-15 py-12 border-b">
|
||||
<view class="text-lg text-dark font-semibold">发送内容</view>
|
||||
<view class="mt-5 text-base text-gray break-all">
|
||||
{{ todo.sendContent }}
|
||||
</view>
|
||||
<view v-for="(file, idx) in todo.fileList" :key="idx" class="mt-5 text-base text-gray break-all">
|
||||
【附件】{{ file.file.name }}
|
||||
</view>
|
||||
</view>
|
||||
<template v-if="canEdit">
|
||||
<picker mode="date" :value="formData.planDate" @change="changePlanDate($event)">
|
||||
<view class="flex items-center py-10 px-15 border-b">
|
||||
<view class="text-base text-gray">计划执行时间</view>
|
||||
<view class="flex-grow"></view>
|
||||
<view class="text-base text-dark">{{ formData.planDate }}</view>
|
||||
<view class="flex-shrink-0">
|
||||
<uni-icons type="right" size="16" color="#999999" />
|
||||
</view>
|
||||
</view>
|
||||
</picker>
|
||||
<picker mode="selector" :range="memberList" range-key="name" :value="formData.executorUserId"
|
||||
@change="changeExecutorUserId($event)">
|
||||
<view class="flex items-center py-10 px-15 border-b">
|
||||
<view class="text-base text-gray">执行人</view>
|
||||
<view class="flex-grow"></view>
|
||||
<view class="text-base text-dark">{{ memberMap[formData.executorUserId] || '' }}</view>
|
||||
<view class="flex-shrink-0">
|
||||
<uni-icons type="right" size="16" color="#999999" />
|
||||
</view>
|
||||
</view>
|
||||
</picker>
|
||||
<picker mode="selector" :range="eventTypeList" range-key="label" :value="formData.eventType"
|
||||
@change="changeEventType($event)">
|
||||
<view class="flex items-center py-10 px-15 border-b">
|
||||
<view class="text-base text-gray">回访类型</view>
|
||||
<view class="flex-grow"></view>
|
||||
<view class="text-base text-dark">{{ ToDoEventType[formData.eventType] || '' }}</view>
|
||||
<view class="flex-shrink-0">
|
||||
<uni-icons type="right" size="16" color="#999999" />
|
||||
</view>
|
||||
</view>
|
||||
</picker>
|
||||
<view class="px-15 py-12 border-b">
|
||||
<view class="text-lg text-dark font-semibold">任务内容</view>
|
||||
<view class="mt-5 p-10 border rounded">
|
||||
<textarea :value="formData.taskContent" class="w-full h-120 text-base text-dark" @input="changeTaskContent($event)" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<!-- v-else -->
|
||||
<template v-else>
|
||||
<view class="flex items-center justify-between py-10 px-15 border-b">
|
||||
<view class="text-base text-gray">计划执行时间</view>
|
||||
<view class="text-base text-dark">{{ todo.planDate }}</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between py-10 px-15 border-b">
|
||||
<view class="text-base text-gray">执行人</view>
|
||||
<view class="text-base text-dark">{{ memberMap[todo.executorUserId] || '' }}</view>
|
||||
</view>
|
||||
<view class="px-15 py-12 border-b">
|
||||
<view class="text-lg text-dark font-semibold">回访结果</view>
|
||||
<view class="mt-5 p-10 border rounded">
|
||||
<textarea :value="formData.result" class="w-full h-120 text-base text-dark" @input="changeResult($event)" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="px-15 py-12 border-b">
|
||||
<view class="text-base text-gray">创建人:{{ todo.creatorUserId }}</view>
|
||||
<view class="text-base text-gray">创建时间:{{ todo.createTime }}</view>
|
||||
<view v-if="todo.endTime" class="text-base text-gray">执行时间:{{ todo.endTime }}</view>
|
||||
</view>
|
||||
<template #footer>
|
||||
<button-footer v-if="canEdit" confirmText="保存" :showCancel="false" @confirm="save()" />
|
||||
<button-footer v-else-if="editable" cancelText="取消任务" confirmText="设为完成" @cancel="cancelTask()"
|
||||
@confirm="completeTask()" />
|
||||
<button-footer v-else-if="canWriteResult" confirmText="保存" :showCancel="false" @confirm="saveResult()" />
|
||||
</template>
|
||||
</full-page>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import dayjs from "dayjs";
|
||||
import { statusNames, ToDoEventType, statusClassNames } from '@/baseData';
|
||||
import useGuard from "@/hooks/useGuard.js";
|
||||
import useAccountStore from "@/store/account.js";
|
||||
import api from "@/utils/api.js";
|
||||
import { confirm, toast } from "@/utils/widget";
|
||||
|
||||
import buttonFooter from '@/components/button-footer.vue';
|
||||
import fullPage from '@/components/full-page.vue';
|
||||
|
||||
const { useLoad } = useGuard();
|
||||
const { doctorInfo, account } = storeToRefs(useAccountStore());
|
||||
const todo = ref(null);
|
||||
const team = ref(null);
|
||||
const form = ref({});
|
||||
const isEditTodo = ref(false);
|
||||
const formData = computed(() => ({ ...(todo.value || {}), ...form.value }));
|
||||
|
||||
const teamManager = computed(() => {
|
||||
const memberLeaderList = team.value && Array.isArray(team.value.memberLeaderList) ? team.value.memberLeaderList : [];
|
||||
return doctorInfo.value && memberLeaderList.includes(doctorInfo.value.userid);
|
||||
})
|
||||
const hasPermission = computed(() => {
|
||||
const userid = doctorInfo.value && doctorInfo.value.userid;
|
||||
return userid && todo.value && (userid === todo.value.executorUserId || teamManager.value);
|
||||
})
|
||||
|
||||
const memberList = computed(() => {
|
||||
const memberList = team.value && Array.isArray(team.value.memberList) ? team.value.memberList : [];
|
||||
return memberList.map(i => ({ name: i.anotherName, value: i.userid }))
|
||||
})
|
||||
const memberMap = computed(() => memberList.value.reduce((m, item) => {
|
||||
m[item.value] = item.name;
|
||||
return m
|
||||
}, {}))
|
||||
|
||||
const canEdit = computed(() => isEditTodo.value && ['notStart', 'processing'].includes(todo.value?.eventStatus))
|
||||
|
||||
const canWriteResult = computed(() => {
|
||||
return hasPermission.value && 'treated' === todo.value?.eventStatus;
|
||||
})
|
||||
const editable = computed(() => {
|
||||
const statusRight = ['notStart', 'processing'].includes(todo.value?.eventStatus);
|
||||
return hasPermission.value && statusRight;
|
||||
})
|
||||
|
||||
const eventTypeList = computed(() => Object.keys(ToDoEventType).map((key) => ({ label: ToDoEventType[key], value: key })));
|
||||
|
||||
|
||||
function getVisitPlanStatus(row) {
|
||||
if (row.eventStatus === "untreated" && dayjs().isBefore(dayjs(row.plannedExecutionTime))) {
|
||||
return "notStart";
|
||||
} else if (row.eventStatus === "untreated" && dayjs().isAfter(dayjs(row.plannedExecutionTime)) && dayjs().isBefore(dayjs(row.expireTime))) {
|
||||
return "processing";
|
||||
} else if (row.eventStatus === "treated") {
|
||||
return "treated";
|
||||
} else if (row.eventStatus === "closed") {
|
||||
return "cancelled";
|
||||
} else if (row.eventStatus === "expired" || dayjs().isAfter(row.expireTime)) {
|
||||
return "expired";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function changePlanDate(e) {
|
||||
form.value.planDate = e.detail.value;
|
||||
}
|
||||
|
||||
function changeExecutorUserId(e) {
|
||||
const idx = e.detail.value;
|
||||
const user = memberList[idx];
|
||||
form.value.executorUserId = user.value;
|
||||
}
|
||||
|
||||
function changeEventType(e) {
|
||||
const idx = e.detail.value;
|
||||
const type = eventTypeList[idx];
|
||||
form.value.eventType = type.value;
|
||||
}
|
||||
|
||||
function changeTaskContent(e) {
|
||||
form.value.taskContent = e.detail.value;
|
||||
}
|
||||
|
||||
function changeResult(e) {
|
||||
form.value.result = e.detail.value;
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (!formData.value.eventType) {
|
||||
return toast('请选择回访类型');
|
||||
}
|
||||
if (!formData.value.planDate) {
|
||||
return toast("请选择执行时间");
|
||||
}
|
||||
if (!formData.value.executorUserId) {
|
||||
return toast("请选择处理人");
|
||||
}
|
||||
const taskParams = {
|
||||
eventType: formData.value.eventType,
|
||||
planExecutionTime: dayjs(formData.value.planDate).valueOf(),
|
||||
executorUserId: formData.value.executorUserId,
|
||||
executorTeamId: formData.value.executorTeamId || undefined,
|
||||
executeTeamName: formData.value.executeTeamName || undefined,
|
||||
taskContent: formData.value.taskContent || undefined,
|
||||
}
|
||||
const params = {
|
||||
id: todo.value._id,
|
||||
task: taskParams,
|
||||
corpId: account.value.corpId
|
||||
}
|
||||
const res = await api('updateTaskTodo', params);
|
||||
if (res && res.success) {
|
||||
await toast('保存成功');
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
toast(res?.message || '保存失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelTask() {
|
||||
await confirm('确定取消该回访任务吗');
|
||||
const res = await api('setTodoStatus', {
|
||||
id: todo.value._id,
|
||||
eventStatus: 'closed',
|
||||
result: formData.value.result || '已取消',
|
||||
userId: doctorInfo.value.userid,
|
||||
})
|
||||
if (res && res.success) {
|
||||
await toast('取消成功');
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
toast(res?.message || '取消失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function completeTask() {
|
||||
await confirm('确定完成该回访任务吗');
|
||||
const res = await api('setTodoStatus', {
|
||||
id: todo.value._id,
|
||||
eventStatus: 'treated',
|
||||
result: formData.value.result || '已完成',
|
||||
userId: doctorInfo.value.userid,
|
||||
})
|
||||
if (res && res.success) {
|
||||
await toast('操作成功');
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
toast(res?.message || '操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function saveResult() {
|
||||
if (typeof formData.value.result !== 'string' || formData.value.result.trim() === '') {
|
||||
return toast('请填写回访结果')
|
||||
}
|
||||
const res = await api('updateTaskTodoResult', {
|
||||
id: todo.value._id,
|
||||
result: formData.value.result.trim(),
|
||||
})
|
||||
if (res && res.success) {
|
||||
await toast('操作成功');
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
toast(res?.message || '操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function getDetail(id) {
|
||||
const res = await api('getTodoById', { id, corpId: account.value.corpId });
|
||||
if (res && res.success) {
|
||||
const eventStatus = getVisitPlanStatus(res.data);
|
||||
todo.value = {
|
||||
...res.data,
|
||||
eventTypeLabel: ToDoEventType[res.data.eventType],
|
||||
planDate: res.data.plannedExecutionTime && dayjs(res.data.plannedExecutionTime).isValid() ? dayjs(res.data.plannedExecutionTime).format("YYYY-MM-DD") : "",
|
||||
endTime: res.data.endTime && dayjs(res.data.endTime).isValid() ? dayjs(res.data.endTime).format("YYYY-MM-DD HH:mm") : "",
|
||||
createTime: res.data.createTime && dayjs(res.data.createTime).isValid() ? dayjs(res.data.createTime).format("YYYY-MM-DD HH:mm") : "",
|
||||
eventStatus,
|
||||
eventStatusLabel: statusNames[eventStatus],
|
||||
fileList: Array.isArray(res.data.fileList) ? res.data.fileList.filter(i => i && i.file && i.file.name) : []
|
||||
};
|
||||
if (todo.value.executeTeamId) {
|
||||
getTeamDetail(todo.value.executeTeamId)
|
||||
}
|
||||
console.log(ToDoEventType, res.data.eventType, ToDoEventType[res.data.eventType])
|
||||
} else {
|
||||
toast(res?.message || '获取数据失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function getTeamDetail(teamId) {
|
||||
const res = await api('getTeamData', { teamId, corpId: account.value.corpId });
|
||||
if (res && res.success) {
|
||||
team.value = res.data;
|
||||
} else {
|
||||
await toast(res.message || '获取团队信息失败')
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
|
||||
useLoad(opts => {
|
||||
console.clear()
|
||||
isEditTodo.value = opts.editMode === 'YES';
|
||||
getDetail(opts.id);
|
||||
})
|
||||
|
||||
</script>
|
||||
<style>
|
||||
.mt-5 {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.h-120 {
|
||||
height: 240rpx;
|
||||
}
|
||||
</style>
|
||||
@ -76,16 +76,17 @@
|
||||
</template>
|
||||
|
||||
<scroll-view v-if="list.length" scroll-y="true" class="h-full">
|
||||
<view v-for="i in list" :key="i._id" class="mb-10 shadow-lg bg-white">
|
||||
<view v-for="i in list" :key="i._id" class="mb-10 shadow-lg bg-white" @click="toTodoDetail(i._id)">
|
||||
<view class="flex items-center justify-between px-15 py-10 border-b">
|
||||
<view class="text-base text-dark">计划执行: {{ i.planDate }}</view>
|
||||
<view class="flex items-center">
|
||||
<view class="text-base text-dark">患者: {{ i.customerName }}</view>
|
||||
<view class="flex items-center" @click.stop="toTodoDetail(i._id, true)">
|
||||
<view class="mr-5 text-base text-dark">患者: {{ i.customerName }}</view>
|
||||
<image class="icon-edit" src="/static/work/edit-pen.svg" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="py-10 px-15 flex items-center">
|
||||
<view class="mr-5 text-lg font-semibold">{{ i.eventTypeLabel }}</view>
|
||||
<view class="bg-opacity px-10 py-3 leading-normal text-base rounded overflow-hidden"
|
||||
<view class="bg-light-text-color px-10 py-3 leading-normal text-base rounded overflow-hidden"
|
||||
:class="statusClassNames[i.eventStatus] || 'text-gray'">
|
||||
{{ i.eventStatusLabel }}
|
||||
</view>
|
||||
@ -97,7 +98,8 @@
|
||||
发送内容:{{ file.file.name }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-primary px-10 py-3 text-base text-white rounded-sm">发送</view>
|
||||
<view v-if="doctorInfo && doctorInfo.userid && i.executorUserId && doctorInfo.userid === i.executorUserId"
|
||||
class="bg-primary px-10 py-3 text-base text-white rounded-sm">发送</view>
|
||||
</view>
|
||||
<view class="mt-10 px-15 text-base leading-normal text-gray truncate">
|
||||
{{ i.executorUserName }}({{ i.executeTeamName }})
|
||||
@ -210,6 +212,12 @@ function handleCert() {
|
||||
}
|
||||
}
|
||||
|
||||
function toTodoDetail(id, editMode = false) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/work/todo/todo-detail?id=${id}&editMode=${editMode ? 'YES' : ''}`
|
||||
})
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
if (!doctorInfo.value || !doctorInfo.value.userid) {
|
||||
return
|
||||
@ -255,7 +263,6 @@ useLoad(() => {
|
||||
});
|
||||
|
||||
useShow(async () => {
|
||||
console.log("工作台页面加!!!@@@载");
|
||||
await getDoctorInfo();
|
||||
changePage(1)
|
||||
})
|
||||
@ -362,23 +369,8 @@ useShow(async () => {
|
||||
height: 42rpx;
|
||||
}
|
||||
|
||||
.bg-opacity {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bg-opacity::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.2;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.py-3 {
|
||||
padding-top: 6rpx;
|
||||
padding-bottom: 6rpx;
|
||||
.icon-edit {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
</style>
|
||||
@ -13,27 +13,27 @@ export default [
|
||||
},
|
||||
{
|
||||
path: 'pages/message/message',
|
||||
meta: { title: '消息', login: false }
|
||||
meta: { title: '消息', login: false }
|
||||
},
|
||||
{
|
||||
path: 'pages/message/common-phrases',
|
||||
meta: { title: '常用语', login: false }
|
||||
meta: { title: '常用语', login: false }
|
||||
},
|
||||
{
|
||||
path: 'pages/message/article-list',
|
||||
meta: { title: '宣教文章', login: false }
|
||||
meta: { title: '宣教文章', login: false }
|
||||
},
|
||||
{
|
||||
path: 'pages/message/article-detail',
|
||||
meta: { title: '宣教文章', login: false }
|
||||
meta: { title: '宣教文章', login: false }
|
||||
},
|
||||
{
|
||||
path: 'pages/message/survey-list',
|
||||
meta: { title: '问卷列表', login: false }
|
||||
meta: { title: '问卷列表', login: false }
|
||||
},
|
||||
{
|
||||
path: 'pages/webview/webview',
|
||||
meta: { title: '预览', login: false }
|
||||
meta: { title: '预览', login: false }
|
||||
},
|
||||
{
|
||||
path: 'pages/message/index',
|
||||
@ -176,9 +176,13 @@ export default [
|
||||
{
|
||||
path: 'pages/work/team/detail/team-detail',
|
||||
meta: { title: '团队信息', login: true }
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'pages/work/service/contact-service',
|
||||
meta: { title: '联系企微客服' }
|
||||
},
|
||||
{
|
||||
path: 'pages/work/todo/todo-detail',
|
||||
meta: { title: '待办详情' }
|
||||
}
|
||||
]
|
||||
|
||||
1
static/work/edit-pen.svg
Normal file
1
static/work/edit-pen.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1770024483535" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4801" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M958.171429 950.857143H62.902857a36.571429 36.571429 0 1 0 0 73.142857h895.268572a36.571429 36.571429 0 1 0 0-73.142857zM84.845714 917.211429h10.971429l292.571428-87.771429a34.377143 34.377143 0 0 0 15.36-9.508571L976.457143 249.417143a68.754286 68.754286 0 0 0 0-98.011429L844.068571 19.748571a73.142857 73.142857 0 0 0-99.474285 0L171.885714 585.142857a35.108571 35.108571 0 0 0-8.045714 11.702857L50.468571 866.011429a37.302857 37.302857 0 0 0 34.377143 51.2zM795.062857 73.142857l125.805714 125.074286L830.171429 288.914286 705.097143 163.108571zM228.205714 635.611429l424.96-419.84 126.537143 125.074285-422.765714 418.377143-206.994286 62.902857z" fill="#0074ff" p-id="4802"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@ -121,7 +121,8 @@ const urlsConfig = {
|
||||
// 客户流转记录
|
||||
customerTransferRecord: 'customerTransferRecord',
|
||||
// sendConsultRejectedMessage: "sendConsultRejectedMessage"
|
||||
getTeamTodos: 'getTeamTodos'
|
||||
getTeamTodos: 'getTeamTodos',
|
||||
updateTaskTodo:'updateTaskTodo'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user