Compare commits
No commits in common. "main" and "dev-hjf" have entirely different histories.
14
App.vue
14
App.vue
@ -40,7 +40,7 @@ export default {
|
||||
if (success) {
|
||||
console.log("IM 初始化成功");
|
||||
// IM 初始化成功后,设置全局未读消息监听
|
||||
// globalUnreadListenerManager.setup();
|
||||
globalUnreadListenerManager.setup();
|
||||
} else {
|
||||
console.warn("IM 初始化失败");
|
||||
}
|
||||
@ -76,17 +76,6 @@ page {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.inset-0 {
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
@ -191,7 +180,6 @@ page {
|
||||
.pt-5 {
|
||||
padding-top: 10rpx;
|
||||
}
|
||||
|
||||
.pt-15 {
|
||||
padding-top: 30rpx;
|
||||
}
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
import { computed, ref, watch } from "vue";
|
||||
import useDebounce from '@/utils/useDebounce'
|
||||
|
||||
export default function usePageList(callback, options = {}) {
|
||||
const keyword = ref('')
|
||||
const list = ref([])
|
||||
const page = ref(1)
|
||||
const pageSize = ref(options.pageSize || 20)
|
||||
const pages = ref(0);
|
||||
const loading = ref(false)
|
||||
const total = ref(0)
|
||||
|
||||
const hasMore = computed(() => page.value < pages.value)
|
||||
|
||||
const handleKeywordChange = useDebounce(() => {
|
||||
getList()
|
||||
}, options.debounce || 1000)
|
||||
|
||||
function changePage(p) {
|
||||
if (loading.value) return
|
||||
page.value = p
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
typeof callback === 'function' && callback()
|
||||
}
|
||||
|
||||
watch(keyword, handleKeywordChange);
|
||||
|
||||
return { total, page, pageSize, keyword, list, pages, changePage, loading, hasMore }
|
||||
|
||||
}
|
||||
449
pages.json
449
pages.json
@ -1,236 +1,215 @@
|
||||
{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/home/home",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页",
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/article/article-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的宣教",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/article/article-cate-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康宣教",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/survey/survey-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的问卷",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/survey/fill",
|
||||
"style": {
|
||||
"navigationBarTitleText": "问卷",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/rate/rate-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "服务评价",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/rate/rate-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "服务评价",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/message",
|
||||
"style": {
|
||||
"navigationBarTitleText": "消息",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "聊天",
|
||||
"enablePullDownRefresh": false,
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/login",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/redirect-page",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/agreement",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/archive/archive-manage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "档案管理",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/archive/edit-archive",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新增档案",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/archive/archive-result",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团队服务",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/health/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康信息",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/health/record",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康信息",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/library/diagnosis-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择诊断",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/team/team-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团队介绍",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/team/homepage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人主页",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/team/friend",
|
||||
"style": {
|
||||
"navigationBarTitleText": "添加好友",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/web-view/web-view",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/mine",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/contact",
|
||||
"style": {
|
||||
"navigationBarTitleText": "联系客服",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/common/privacy",
|
||||
"style": {
|
||||
"navigationBarTitleText": "隐私政策",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/common/agreement",
|
||||
"style": {
|
||||
"navigationBarTitleText": "用户协议",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/article/article-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "宣教文章",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/article/send-article",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择宣教文章",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarTitleText": "uni-app",
|
||||
"navigationBarBackgroundColor": "#065bd6",
|
||||
"backgroundColor": "#065bd6"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#666666",
|
||||
"selectedColor": "#007aff",
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderStyle": "white",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/home/home",
|
||||
"iconPath": "static/tabbar/home.png",
|
||||
"selectedIconPath": "static/tabbar/home_selected.png",
|
||||
"text": "服务"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/message/message",
|
||||
"iconPath": "static/tabbar/consult.png",
|
||||
"selectedIconPath": "static/tabbar/consult_selected.png",
|
||||
"text": "咨询"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/mine/mine",
|
||||
"iconPath": "static/tabbar/mine.png",
|
||||
"selectedIconPath": "static/tabbar/mine_selected.png",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniIdRouter": {}
|
||||
}
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/home/home",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页",
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/article/article-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的宣教",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/article/article-cate-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康宣教",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/survey/survey-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的问卷",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/message",
|
||||
"style": {
|
||||
"navigationBarTitleText": "消息",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "聊天",
|
||||
"enablePullDownRefresh": false,
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/login",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/redirect-page",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/agreement",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/archive/archive-manage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "档案管理",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/archive/edit-archive",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新增档案",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/archive/archive-result",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团队服务",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/health/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康信息",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/health/record",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康信息",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/library/diagnosis-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择诊断",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/team/team-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团队介绍",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/team/homepage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人主页",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/team/friend",
|
||||
"style": {
|
||||
"navigationBarTitleText": "添加好友",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/web-view/web-view",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/mine",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/contact",
|
||||
"style": {
|
||||
"navigationBarTitleText": "联系客服",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/common/privacy",
|
||||
"style": {
|
||||
"navigationBarTitleText": "隐私政策",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/common/agreement",
|
||||
"style": {
|
||||
"navigationBarTitleText": "用户协议",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/article/article-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "宣教文章",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/article/send-article",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择宣教文章",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarTitleText": "uni-app",
|
||||
"navigationBarBackgroundColor": "#065bd6",
|
||||
"backgroundColor": "#065bd6"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#666666",
|
||||
"selectedColor": "#007aff",
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderStyle": "white",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/home/home",
|
||||
"iconPath": "static/tabbar/home.png",
|
||||
"selectedIconPath": "static/tabbar/home_selected.png",
|
||||
"text": "服务"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/message/message",
|
||||
"iconPath": "static/tabbar/consult.png",
|
||||
"selectedIconPath": "static/tabbar/consult_selected.png",
|
||||
"text": "咨询"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/mine/mine",
|
||||
"iconPath": "static/tabbar/mine.png",
|
||||
"selectedIconPath": "static/tabbar/mine_selected.png",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniIdRouter": {}
|
||||
}
|
||||
@ -58,13 +58,12 @@ const { useLoad, useShow } = useGuard();
|
||||
const { account } = storeToRefs(useAccount());
|
||||
const corpId = ref('');
|
||||
const teamId = ref('');
|
||||
const corpUserId = ref('')
|
||||
const enableHis = ref(false);
|
||||
const customers = ref([]);
|
||||
|
||||
function addArchive() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/archive/edit-archive?corpUserId=${corpUserId.value}&teamId=${teamId.value}&corpId=${corpId.value}`
|
||||
url: `/pages/archive/edit-archive?teamId=${teamId.value}&corpId=${corpId.value}`
|
||||
})
|
||||
}
|
||||
|
||||
@ -97,7 +96,6 @@ async function unBindArchive(customer) {
|
||||
useLoad(options => {
|
||||
teamId.value = options.teamId;
|
||||
corpId.value = options.corpId;
|
||||
corpUserId.value = options.corpUserId;
|
||||
})
|
||||
|
||||
useShow(() => {
|
||||
|
||||
@ -43,7 +43,6 @@ const { account, externalUserId } = storeToRefs(useAccount());
|
||||
const { getExternalUserId } = useAccount()
|
||||
const corpId = ref('');
|
||||
const corpName = ref('');
|
||||
const corpUserId = ref('');
|
||||
const customer = ref({});
|
||||
const customerId = ref('');
|
||||
const customers = ref([]);
|
||||
@ -53,6 +52,7 @@ const formItems = ref([]);
|
||||
const loading = ref(false);
|
||||
const teamId = ref('');
|
||||
const tempRef = ref(null);
|
||||
const customerArchive = ref(null);
|
||||
const verifyVisible = ref(false);
|
||||
const visible = ref(false);
|
||||
|
||||
@ -64,19 +64,14 @@ function change({ title, value }) {
|
||||
if (title) {
|
||||
form.value[title] = value;
|
||||
}
|
||||
if (title == 'idCard') {
|
||||
const [isIdCard, birthday, gender] = validate.isChinaId(value);
|
||||
if (isIdCard) {
|
||||
form.value.birthday = birthday;
|
||||
form.value.sex = gender == 'MALE' ? '男' : '女';
|
||||
const age = dayjs().diff(birthday, 'year');
|
||||
form.value.age = Math.max(1, age);
|
||||
}
|
||||
} else if (title === 'birthday' && formItems.value.some(i => i.title === 'age') && value && dayjs(value).valueOf()) {
|
||||
const age = dayjs().diff(value, 'year');
|
||||
if (title !== 'idCard') return;
|
||||
const [isIdCard, birthday, gender] = validate.isChinaId(value);
|
||||
if (isIdCard) {
|
||||
form.value.birthday = birthday;
|
||||
form.value.sex = gender == 'MALE' ? '男' : '女';
|
||||
const age = dayjs().diff(birthday, 'year');
|
||||
form.value.age = Math.max(1, age);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function confirm() {
|
||||
@ -141,15 +136,10 @@ async function addArchive() {
|
||||
externalUserId: externalUserId.value,
|
||||
realUnionid: account.value.unionid || '',
|
||||
}
|
||||
if (externalUserId.value) {
|
||||
const corpUserId = await getResponsiblePerson();
|
||||
if (corpUserId) {
|
||||
params.personResponsibles = [{ corpUserId, teamId: teamId.value }]
|
||||
}
|
||||
}
|
||||
loading.value = false;
|
||||
const res = await api('addCustomer', { params });
|
||||
if (res && res.success) {
|
||||
set('home-invite-teamId', teamId.value);
|
||||
uni.$emit('reloadTeamCustomers')
|
||||
uni.redirectTo({
|
||||
url: `/pages/archive/archive-result?corpId=${corpId.value}&teamId=${teamId.value}`
|
||||
@ -159,21 +149,11 @@ async function addArchive() {
|
||||
}
|
||||
}
|
||||
|
||||
async function getResponsiblePerson() {
|
||||
const res = await api('getResponsiblePerson', { corpId: corpId.value, teamId: teamId.value, externalUserId: externalUserId.value, corpUserId: corpUserId.value });
|
||||
return res && res.data ? res.data : ''
|
||||
}
|
||||
|
||||
async function bindArchive(customerId) {
|
||||
let responsiblePerson = '';
|
||||
if (externalUserId.value) {
|
||||
const corpUserId = await getResponsiblePerson();
|
||||
responsiblePerson = corpUserId || '';
|
||||
}
|
||||
const res = await api('bindMiniAppArchive', { id: customerId, corpId: corpId.value, teamId: teamId.value, miniAppId: account.value.openid, externalUserId: externalUserId.value, responsiblePerson });
|
||||
const res = await api('bindMiniAppArchive', { id: customerId, corpId: corpId.value, teamId: teamId.value, miniAppId: account.value.openid });
|
||||
if (res && res.success) {
|
||||
await toast('绑定成功');
|
||||
uni.$emit('reloadTeamCustomers')
|
||||
set('home-invite-teamId', teamId.value);
|
||||
uni.switchTab({
|
||||
url: '/pages/home/home'
|
||||
})
|
||||
@ -282,7 +262,6 @@ async function unBindArchive() {
|
||||
|
||||
onLoad(options => {
|
||||
customerId.value = options.id || '';
|
||||
corpUserId.value = options.corpUserId || '';
|
||||
uni.setNavigationBarTitle({ title: customerId.value ? '编辑档案' : '新增档案' })
|
||||
})
|
||||
|
||||
|
||||
@ -1,34 +1,31 @@
|
||||
<template>
|
||||
<full-page mainClass="bg-white">
|
||||
<view class="article-detail-page">
|
||||
<view v-if="loading" class="loading-container">
|
||||
<uni-icons type="spinner-cycle" size="40" color="#999" />
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<view v-else-if="error" class="error-container">
|
||||
<text class="error-text">{{ error }}</text>
|
||||
<button class="retry-btn" @click="loadArticle">重试</button>
|
||||
</view>
|
||||
|
||||
<scroll-view v-else scroll-y class="article-content">
|
||||
<view class="article-header">
|
||||
<text class="article-title">{{ articleData.title }}</text>
|
||||
<text class="article-date">{{ articleData.date }}</text>
|
||||
</view>
|
||||
<view class="article-body">
|
||||
<view class="rich-text-wrapper">
|
||||
<rich-text :nodes="articleData.content"></rich-text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="article-detail-page">
|
||||
<view v-if="loading" class="loading-container">
|
||||
<uni-icons type="spinner-cycle" size="40" color="#999" />
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
</full-page>
|
||||
|
||||
<view v-else-if="error" class="error-container">
|
||||
<text class="error-text">{{ error }}</text>
|
||||
<button class="retry-btn" @click="loadArticle">重试</button>
|
||||
</view>
|
||||
|
||||
<scroll-view v-else scroll-y class="article-content">
|
||||
<view class="article-header">
|
||||
<text class="article-title">{{ articleData.title }}</text>
|
||||
<text class="article-date">{{ articleData.date }}</text>
|
||||
</view>
|
||||
<view class="article-body">
|
||||
<view class="rich-text-wrapper">
|
||||
<rich-text :nodes="articleData.content"></rich-text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import FullPage from "@/components/full-page.vue";
|
||||
import api from "@/utils/api.js";
|
||||
import { ref } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
@ -43,15 +40,15 @@ const articleData = ref({
|
||||
});
|
||||
|
||||
let articleId = "";
|
||||
const corpId = ref("");
|
||||
const corpId = ref('')
|
||||
|
||||
const markArticleRead = async (sendId) => {
|
||||
const markArticleRead = async () => {
|
||||
const unionid = account.value?.unionid;
|
||||
if (!unionid || !articleId) return;
|
||||
try {
|
||||
await api(
|
||||
"addArticleReadRecord",
|
||||
{ corpId: corpId.value, articleId, unionid, sendId },
|
||||
{ corpId: corpId.value, articleId, unionid },
|
||||
false
|
||||
);
|
||||
} catch (err) {
|
||||
@ -94,10 +91,7 @@ const loadArticle = async () => {
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
try {
|
||||
const res = await api("getArticle", {
|
||||
id: articleId,
|
||||
corpId: corpId.value,
|
||||
});
|
||||
const res = await api("getArticle", { id: articleId, corpId: corpId.value });
|
||||
|
||||
if (res.success && res.data) {
|
||||
// 格式化日期
|
||||
@ -130,7 +124,7 @@ onLoad((options) => {
|
||||
corpId.value = options.corpId;
|
||||
if (options.id) {
|
||||
articleId = options.id;
|
||||
markArticleRead(options.sendId || '');
|
||||
markArticleRead();
|
||||
loadArticle();
|
||||
} else {
|
||||
error.value = "文章信息不完整";
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<full-page pageClass="bg-gray-100">
|
||||
<template #header>
|
||||
<scroll-view scroll-x class="bg-white whitespace-nowrap px-15 py-10 sticky top-0 z-10 w-full"
|
||||
:show-scrollbar="false">
|
||||
<view v-for="(tab, index) in tabs" :key="index"
|
||||
class="inline-block px-15 py-5 mr-10 text-sm rounded-full border transition-colors" :class="[
|
||||
activeTab === tab.value
|
||||
? 'bg-orange-100 text-orange-500 border-orange-500'
|
||||
: 'bg-white text-gray-600 border-gray-200'
|
||||
]" @click="selectTab(tab.value)">
|
||||
{{ tab.name }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
<view class="bg-gray-100 min-h-screen">
|
||||
<!-- Filter Tabs -->
|
||||
<scroll-view scroll-x class="bg-white whitespace-nowrap px-15 py-10 sticky top-0 z-10 w-full"
|
||||
:show-scrollbar="false">
|
||||
<view v-for="(tab, index) in tabs" :key="index"
|
||||
class="inline-block px-15 py-5 mr-10 text-sm rounded-full border transition-colors" :class="[
|
||||
activeTab === tab.value
|
||||
? 'bg-orange-100 text-orange-500 border-orange-500'
|
||||
: 'bg-white text-gray-600 border-gray-200'
|
||||
]" @click="selectTab(tab.value)">
|
||||
{{ tab.name }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- Article List -->
|
||||
<view v-if="loading && articles.length === 0" class="loading-container">
|
||||
<uni-icons type="spinner-cycle" size="30" color="#999" />
|
||||
@ -74,7 +74,7 @@
|
||||
没有更多了
|
||||
</view>
|
||||
</view>
|
||||
</full-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -84,8 +84,6 @@ import { storeToRefs } from "pinia";
|
||||
import dayjs from "dayjs";
|
||||
import api from "@/utils/api.js";
|
||||
import useAccountStore from "@/store/account.js";
|
||||
|
||||
import fullPage from "@/components/full-page.vue";
|
||||
import EmptyData from "@/components/empty-data.vue";
|
||||
|
||||
const { account, openid } = storeToRefs(useAccountStore());
|
||||
@ -100,7 +98,6 @@ const pageSize = 20;
|
||||
const loading = ref(false);
|
||||
const inited = ref(false);
|
||||
const corpId = ref('');
|
||||
const teamId = ref('');
|
||||
|
||||
const selectTab = async (customerId) => {
|
||||
if (activeTab.value === customerId) return;
|
||||
@ -142,7 +139,6 @@ const mapRowToView = (row) => {
|
||||
};
|
||||
|
||||
const loadArticleList = async (reset = false) => {
|
||||
const customerIds = tabs.value.map(i => i.value).filter(Boolean);
|
||||
if (loading.value) return;
|
||||
const unionid = account.value?.unionid;
|
||||
const miniAppId = openid.value || uni.getStorageSync("openid");
|
||||
@ -156,12 +152,6 @@ const loadArticleList = async (reset = false) => {
|
||||
articles.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
if (customerIds.length === 0) {
|
||||
page.value = 1;
|
||||
articles.value = [];
|
||||
total.value = 0;
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
@ -171,10 +161,8 @@ const loadArticleList = async (reset = false) => {
|
||||
miniAppId,
|
||||
page: page.value,
|
||||
pageSize,
|
||||
customerIds,
|
||||
teamId: teamId.value
|
||||
};
|
||||
if (activeTab.value) params.customerIds = [activeTab.value];
|
||||
if (activeTab.value) params.customerId = activeTab.value;
|
||||
|
||||
const res = await api("getMiniAppReceivedArticleList", params);
|
||||
if (res && res.success) {
|
||||
@ -196,11 +184,10 @@ const loadArticleList = async (reset = false) => {
|
||||
|
||||
function goToDetail(item) {
|
||||
if (!item?.articleId) return;
|
||||
uni.navigateTo({ url: `/pages/article/article-detail?sendId=${item._id}&id=${item.articleId}&corpId=${corpId.value}` });
|
||||
uni.navigateTo({ url: `/pages/article/article-detail?id=${item.articleId}&corpId=${corpId.value}` });
|
||||
}
|
||||
onLoad(opts => {
|
||||
corpId.value = opts.corpId;
|
||||
teamId.value = opts.teamId;
|
||||
})
|
||||
|
||||
onShow(async () => {
|
||||
|
||||
@ -4,9 +4,8 @@
|
||||
|
||||
<view class="consult-grid">
|
||||
<view class="consult-item" v-for="item in consultItems" :key="item.id" @click="handleItemClick(item)">
|
||||
<view class="relative item-icon">
|
||||
<view class="item-icon">
|
||||
<image :src="item.icon" class="icon-img" mode="aspectFill" />
|
||||
<view v-if="badgeMap[item.badge]" class="item-dot"></view>
|
||||
</view>
|
||||
<view class="item-label">{{ item.label }}</view>
|
||||
</view>
|
||||
@ -19,7 +18,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { ref, computed } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import useAccount from "@/store/account";
|
||||
import api from "@/utils/api";
|
||||
@ -47,8 +46,6 @@ const props = defineProps({
|
||||
|
||||
const { account } = storeToRefs(useAccount());
|
||||
const consultantPopup = ref(null);
|
||||
const badgeMap = ref({});
|
||||
let loading = false;
|
||||
|
||||
const consultItems = ref([
|
||||
{
|
||||
@ -56,28 +53,24 @@ const consultItems = ref([
|
||||
label: "聊天咨询",
|
||||
icon: "/static/home/chat-consult.png",
|
||||
needSelectConsultant: true,
|
||||
badge: 'chat'
|
||||
},
|
||||
{
|
||||
id: "education",
|
||||
label: "我的宣教",
|
||||
icon: "/static/home/my-education.png",
|
||||
path: "/pages/article/article-list",
|
||||
badge: 'article'
|
||||
},
|
||||
{
|
||||
id: "survey",
|
||||
label: "我的问卷",
|
||||
icon: "/static/home/my-questionnaire.png",
|
||||
path: "/pages/survey/survey-list",
|
||||
badge: 'survey'
|
||||
},
|
||||
{
|
||||
id: "rating",
|
||||
label: "服务评价",
|
||||
icon: "/static/home/service-rating.png",
|
||||
path: "/pages/rate/rate-list",
|
||||
badge: 'rate'
|
||||
path: "",
|
||||
},
|
||||
]);
|
||||
|
||||
@ -163,32 +156,6 @@ function handleAddNewArchive() {
|
||||
url: `/pages/archive/edit-archive?corpId=${props.corpId}&teamId=${props.teamId}`,
|
||||
});
|
||||
}
|
||||
|
||||
async function getBadgeCount() {
|
||||
if (loading) return;
|
||||
loading = true;
|
||||
const customerIds = props.customers.map((item) => item._id);
|
||||
if (customerIds.length === 0) {
|
||||
badgeMap.value = {};
|
||||
loading = false
|
||||
return
|
||||
}
|
||||
const res = await api('getMiniAppHomeStats', { corpId: props.corpId, teamId: props.teamId, customerIds }, false)
|
||||
const data = res?.data || {};
|
||||
const article = typeof data.article === 'number' ? data.article : 0;
|
||||
const survey = typeof data.survey === 'number' ? data.survey : 0;
|
||||
const rate = typeof data.rate === 'number' ? data.rate : 0;
|
||||
badgeMap.value = { article, survey, rate };
|
||||
loading = false;
|
||||
}
|
||||
|
||||
watch(() => [props.customers, props.teamId, props.corpId], n => {
|
||||
getBadgeCount()
|
||||
}, { immediate: true })
|
||||
|
||||
defineExpose({
|
||||
getBadgeCount,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -237,16 +204,6 @@ defineExpose({
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.item-dot {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: red;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.consult-item:active .item-icon {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
@ -82,10 +82,6 @@ const props = defineProps({
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
corpUserIds: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
customers: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
@ -97,10 +93,8 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:customers"]);
|
||||
let loading = false;
|
||||
|
||||
const { account, externalUserId } = storeToRefs(useAccount());
|
||||
const { getExternalUserId } = useAccount()
|
||||
const { account } = storeToRefs(useAccount());
|
||||
const current = ref(null);
|
||||
const customers = ref([]);
|
||||
|
||||
@ -148,7 +142,8 @@ function toHealthList() {
|
||||
if (canAuth.value) {
|
||||
toast("请先授权本服务团队");
|
||||
} else {
|
||||
const name = `${current.value.name} ${current.value.relationship || ""}`;
|
||||
const name = `${current.value.name} ${current.value.relationship ? `(${current.value.relationship})` : ""
|
||||
}`;
|
||||
uni.navigateTo({
|
||||
url: `/pages/health/list?teamId=${props.team.teamId}&corpId=${props.corpId
|
||||
}&id=${current.value._id}&name=${encodeURIComponent(name)}`,
|
||||
@ -161,20 +156,17 @@ function toggle(i) {
|
||||
}
|
||||
|
||||
function toManagePage() {
|
||||
const corpUserId = props.corpUserIds && props.corpUserIds[props.team.teamId] ? props.corpUserIds[props.team.teamId] : "";
|
||||
uni.navigateTo({
|
||||
url: `/pages/archive/archive-manage?corpUserId=${corpUserId}&corpId=${props.corpId}&teamId=${props.team.teamId}`,
|
||||
url: `/pages/archive/archive-manage?corpId=${props.corpId}&teamId=${props.team.teamId}`,
|
||||
});
|
||||
}
|
||||
|
||||
async function auth() {
|
||||
await confirm(`是否授权${props.team.name}提供服务`);
|
||||
const corpUserId = await getResponsiblePerson();
|
||||
const res = await api("authCustomerToTeam", {
|
||||
corpId: props.corpId,
|
||||
teamId: props.team.teamId,
|
||||
id: current.value._id,
|
||||
corpUserId
|
||||
});
|
||||
if (res && res.success) {
|
||||
await toast("授权成功");
|
||||
@ -185,12 +177,10 @@ async function auth() {
|
||||
}
|
||||
|
||||
async function getCustomers() {
|
||||
if (loading) return;
|
||||
loading = true
|
||||
const res = await api("getMiniAppCustomers", {
|
||||
miniAppId: account.value.openid,
|
||||
corpId: props.corpId,
|
||||
}, false);
|
||||
});
|
||||
if (res && res.success) {
|
||||
customers.value = res && Array.isArray(res.data) ? res.data : [];
|
||||
const customer = customers.value.find(
|
||||
@ -202,16 +192,6 @@ async function getCustomers() {
|
||||
} else {
|
||||
toast(res.message || "获取档案失败");
|
||||
}
|
||||
loading = false
|
||||
}
|
||||
|
||||
async function getResponsiblePerson() {
|
||||
if (!externalUserId.value) {
|
||||
await getExternalUserId(props.corpId)
|
||||
}
|
||||
const corpUserId = props.corpUserIds && props.corpUserIds[props.team.teamId] ? props.corpUserIds[props.team.teamId] : "";
|
||||
const res = await api('getResponsiblePerson', { corpId: props.corpId, teamId: props.team.teamId, corpUserId, externalUserId: externalUserId.value });
|
||||
return res && res.data ? res.data : ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@ -229,10 +209,6 @@ watch(
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
defineExpose({
|
||||
getCustomers,
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.archive-container {
|
||||
|
||||
@ -5,11 +5,10 @@
|
||||
<team-head :team="team" :teams="teams" @changeTeam="changeTeam" />
|
||||
</template>
|
||||
<view class="home-section home-section--first">
|
||||
<customer-archive ref="archiveRef" :corpId="corpId" :corpUserIds="corpUserIds" :team="team"
|
||||
@update:customers="handleCustomersUpdate" />
|
||||
<customer-archive :corpId="corpId" :team="team" @update:customers="handleCustomersUpdate" />
|
||||
</view>
|
||||
<view class="home-section">
|
||||
<consult ref="consultRef" :corpId="corpId" :teamId="team.teamId" :team="team" :customers="customers" />
|
||||
<consult :corpId="corpId" :teamId="team.teamId" :team="team" :customers="customers" />
|
||||
</view>
|
||||
<!-- <view class="home-section">
|
||||
<team-mate :team="team" />
|
||||
@ -47,9 +46,6 @@ const team = ref(null);
|
||||
const teams = ref([]);
|
||||
const loading = ref(false);
|
||||
const customers = ref([]);
|
||||
const consultRef = ref(null);
|
||||
const archiveRef = ref(null);
|
||||
const corpUserIds = ref({});
|
||||
|
||||
const corpId = computed(() => team.value?.corpId);
|
||||
|
||||
@ -73,14 +69,15 @@ async function changeTeam({ teamId, corpId, corpName }) {
|
||||
}
|
||||
}
|
||||
|
||||
async function getTeams(inviteTeamId = '') {
|
||||
async function getTeams() {
|
||||
loading.value = true;
|
||||
const res = await api('getWxappRelateTeams', { openid: account.value.openid });
|
||||
teams.value = res && Array.isArray(res.data) ? res.data : [];
|
||||
const matchTeamId = inviteTeamId || (team.value ? team.value.teamId : '');
|
||||
const matchTeamId = get('home-invite-teamId') || (team.value ? team.value.teamId : '');
|
||||
const validTeam = teams.value.find(i => i.teamId && i.teamId === matchTeamId);
|
||||
const firstTeam = teams.value[0]
|
||||
if (validTeam || firstTeam) {
|
||||
remove('home-invite-teamId');
|
||||
changeTeam(validTeam || firstTeam);
|
||||
} else {
|
||||
team.value = null;
|
||||
@ -100,24 +97,12 @@ onLoad(() => {
|
||||
|
||||
onShow(async () => {
|
||||
if (!account.value) await login();
|
||||
const inviteTeam = get('home-invite-team-info');
|
||||
remove('home-invite-team-info');
|
||||
if (inviteTeam && inviteTeam.teamId && inviteTeam.corpUserId) {
|
||||
corpUserIds.value[inviteTeam.teamId] = inviteTeam.corpUserId;
|
||||
}
|
||||
if (account.value && account.value.openid) {
|
||||
getTeams(inviteTeam && inviteTeam.teamId ? inviteTeam.teamId : '');
|
||||
getTeams();
|
||||
} else {
|
||||
teams.value = [];
|
||||
}
|
||||
if (consultRef.value && typeof consultRef.value.getBadgeCount === 'function') {
|
||||
consultRef.value.getBadgeCount()
|
||||
}
|
||||
if (archiveRef.value && typeof archiveRef.value.getCustomers === 'function') {
|
||||
archiveRef.value.getCustomers()
|
||||
}
|
||||
});
|
||||
|
||||
watch(account, (n, o) => {
|
||||
if (n && !o) {
|
||||
getTeams();
|
||||
|
||||
@ -110,6 +110,7 @@ async function bindTeam() {
|
||||
}
|
||||
const res1 = await api('getWxAppCustomerCount', { miniAppId: account.value.openid, corpId: team.value.corpId, teamId: team.value.teamId });
|
||||
if (res1 && res1.data > 0) {
|
||||
set('home-invite-teamId', team.value.teamId);
|
||||
toHome();
|
||||
} else {
|
||||
attempToPage(redirectUrl.value)
|
||||
@ -142,7 +143,7 @@ async function attempToPage(url) {
|
||||
onLoad((opts) => {
|
||||
if (opts.source === "teamInvite") {
|
||||
team.value = get("invite-team-info");
|
||||
redirectUrl.value = `/pages/archive/edit-archive?corpUserId=${team.value.corpUserId || ''}&teamId=${team.value.teamId}&corpId=${team.value.corpId}`;
|
||||
redirectUrl.value = `/pages/archive/edit-archive?teamId=${team.value.teamId}&corpId=${team.value.corpId}`;
|
||||
return;
|
||||
}
|
||||
redirectUrl.value = opts.redirectUrl || "";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="flex flex-col justify-center items-center h-full" @click="copy()">
|
||||
<view class="flex flex-col justify-center items-center h-full">
|
||||
<image class="flash-logo" src="/static/logo-plain.png" />
|
||||
</view>
|
||||
</template>
|
||||
@ -19,30 +19,18 @@ const { account } = storeToRefs(useAccountStore());
|
||||
|
||||
const loading = ref(false);
|
||||
const team = ref(null);
|
||||
const opts = ref('');
|
||||
|
||||
function copy() {
|
||||
uni.setClipboardData({
|
||||
data: opts.value || '暂无内容'
|
||||
})
|
||||
}
|
||||
|
||||
async function changeTeam({ teamId, corpId, corpUserId }) {
|
||||
async function changeTeam({ teamId, corpId }) {
|
||||
loading.value = true;
|
||||
const res = await api("getTeamData", { teamId, corpId, withCorpName: true });
|
||||
loading.value = false;
|
||||
if (res && res.data) {
|
||||
team.value = res.data;
|
||||
team.value.corpName = res.data.corpName;
|
||||
set('home-invite-team-info', {
|
||||
teamId: team.value.teamId,
|
||||
corpUserId: corpUserId || ''
|
||||
});
|
||||
if (account.value) {
|
||||
bindTeam(corpUserId)
|
||||
bindTeam()
|
||||
} else {
|
||||
set("invite-team-info", {
|
||||
corpUserId,
|
||||
corpId: team.value.corpId,
|
||||
teamId: team.value.teamId,
|
||||
corpName: team.value.corpName,
|
||||
@ -61,25 +49,25 @@ async function changeTeam({ teamId, corpId, corpUserId }) {
|
||||
}
|
||||
}
|
||||
|
||||
async function bindTeam(corpUserId) {
|
||||
async function bindTeam() {
|
||||
const res = await api('bindWxappWithTeam', { appid, corpId: team.value.corpId, teamId: team.value.teamId, openid: account.value.openid });
|
||||
if (!res || !res.success) {
|
||||
return toast("关联团队失败");
|
||||
}
|
||||
const res1 = await api('getWxAppCustomerCount', { miniAppId: account.value.openid, corpId: team.value.corpId, teamId: team.value.teamId });
|
||||
if (res1 && res1.data > 0) {
|
||||
set('home-invite-teamId', team.value.teamId);
|
||||
uni.switchTab({
|
||||
url: "/pages/home/home",
|
||||
});
|
||||
} else {
|
||||
uni.redirectTo({
|
||||
url: `/pages/archive/edit-archive?corpUserId=${corpUserId || ''}&teamId=${team.value.teamId}&corpId=${team.value.corpId}`
|
||||
url: `/pages/archive/edit-archive?teamId=${team.value.teamId}&corpId=${team.value.corpId}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
opts.value = JSON.stringify(options)
|
||||
const href =
|
||||
typeof options.q === "string" ? decodeURIComponent(options.q) : "";
|
||||
const [, url = ""] = href.split("?");
|
||||
|
||||
@ -407,7 +407,7 @@ $primary-color: #0877F1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12rpx 20rpx;
|
||||
padding-bottom: 40rpx;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
@ -514,7 +514,7 @@ $primary-color: #0877F1;
|
||||
background: #fff;
|
||||
border-top: 1rpx solid #eee;
|
||||
padding: 20rpx 0 20rpx 60rpx;
|
||||
padding-bottom: 40rpx;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
gap: 40rpx 50rpx;
|
||||
flex-wrap: wrap;
|
||||
background-color: #f5f5f5;
|
||||
@ -544,7 +544,7 @@ $primary-color: #0877F1;
|
||||
background-color: white;
|
||||
border-top: 1rpx solid #e0e0e0;
|
||||
padding: 16rpx;
|
||||
margin-bottom: 40rpx;
|
||||
margin-bottom: env(safe-area-inset-bottom);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@ -689,7 +689,7 @@ $primary-color: #0877F1;
|
||||
width: 100%;
|
||||
max-height: 80vh;
|
||||
padding: 20rpx;
|
||||
padding-bottom: calc(20rpx + 40rpx);
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
@ -821,7 +821,7 @@ $primary-color: #0877F1;
|
||||
background-color: white;
|
||||
width: auto;
|
||||
padding: 32rpx 20rpx 48rpx 20rpx;
|
||||
padding-bottom: calc(48rpx + 40rpx);
|
||||
padding-bottom: calc(48rpx + env(safe-area-inset-bottom));
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
|
||||
@ -36,8 +36,6 @@ const handleApply = () => {
|
||||
padding: 20rpx 32rpx;
|
||||
background-color: #f5f5f5;
|
||||
box-sizing: border-box;
|
||||
position:absolute;
|
||||
bottom: 40rpx;
|
||||
}
|
||||
|
||||
.apply-card {
|
||||
|
||||
@ -12,10 +12,11 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(["cancel"]);
|
||||
|
||||
const emit = defineEmits(['cancel']);
|
||||
|
||||
const handleCancel = () => {
|
||||
emit("cancel");
|
||||
emit('cancel');
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -25,8 +26,6 @@ const handleCancel = () => {
|
||||
padding: 20rpx 32rpx;
|
||||
background-color: #f5f5f5;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
bottom: 40rpx;
|
||||
}
|
||||
|
||||
.cancel-card {
|
||||
|
||||
@ -195,9 +195,9 @@ const getArticleData = (message) => {
|
||||
|
||||
// 处理文章点击
|
||||
const handleArticleClick = (message) => {
|
||||
const { articleId, sendId } = getArticleData(message);
|
||||
const { articleId } = getArticleData(message);
|
||||
uni.navigateTo({
|
||||
url: `/pages/article/article-detail?id=${articleId}&sendId=${sendId || ''}&corpId=${props.corpId}`,
|
||||
url: `/pages/article/article-detail?id=${articleId}&corpId=${props.corpId}`,
|
||||
});
|
||||
};
|
||||
|
||||
@ -207,7 +207,6 @@ const getSurveyData = (message) => {
|
||||
if (message.payload && message.payload.data) {
|
||||
const data = JSON.parse(message.payload.data);
|
||||
return {
|
||||
...data,
|
||||
title: data.title || "填写问卷",
|
||||
desc: data.desc || "请填写问卷",
|
||||
url: data.url || "",
|
||||
@ -222,26 +221,18 @@ const getSurveyData = (message) => {
|
||||
desc: "请填写问卷",
|
||||
url: "",
|
||||
imgUrl: "",
|
||||
err: true
|
||||
};
|
||||
};
|
||||
|
||||
// 处理问卷点击
|
||||
const handleSurveyClick = (message) => {
|
||||
const surveyData = getSurveyData(message);
|
||||
if (surveyData.err) {
|
||||
return
|
||||
}
|
||||
if (surveyData.isSystem) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/web-view/web-view?src=${encodeURIComponent(surveyData.url)}`
|
||||
})
|
||||
return
|
||||
}
|
||||
if (surveyData.answerId) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/survey/fill?name=${surveyData.name}&memberId=${surveyData.memberId}&surveryId=${surveyData.surveryId}&corpId=${surveyData.corpId}&answerId=${surveyData.answerId}`
|
||||
})
|
||||
if (surveyData.url) {
|
||||
// 跳转到问卷填写页面或打开外部链接
|
||||
console.log("打开问卷:", surveyData.url);
|
||||
// uni.navigateTo({
|
||||
// url: `/pages/survey/fill?url=${encodeURIComponent(surveyData.url)}`
|
||||
// });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -58,8 +58,8 @@ export default function useGroupChat(groupID) {
|
||||
return member.isTeamMember ? '/static/default-avatar.png' : '/static/default-patient-avatar.png'
|
||||
}
|
||||
// 获取群聊信息和成员头像
|
||||
async function getGroupInfo(id) {
|
||||
const gid = id || groupID;
|
||||
async function getGroupInfo() {
|
||||
const gid = typeof groupID === 'string' ? groupID : groupID.value
|
||||
if (!gid) return
|
||||
|
||||
try {
|
||||
@ -73,8 +73,10 @@ export default function useGroupChat(groupID) {
|
||||
status: groupResult.data.orderStatus || 'active',
|
||||
teamId: groupResult.data.teamId
|
||||
}
|
||||
|
||||
// 2. 如果有teamId,获取团队成员头像和名称
|
||||
if (groupResult.data.teamId) {
|
||||
|
||||
const memberMap = await teamStore.getTeamMemberAvatarsAndName(groupResult.data.teamId)
|
||||
|
||||
// 3. 存储团队成员ID列表
|
||||
@ -110,14 +112,13 @@ export default function useGroupChat(groupID) {
|
||||
}
|
||||
}
|
||||
|
||||
// onShow(() => {
|
||||
onShow(() => {
|
||||
getGroupInfo()
|
||||
})
|
||||
|
||||
// getGroupInfo()
|
||||
// })
|
||||
|
||||
// onUnload(() => {
|
||||
// // 清理资源
|
||||
// })
|
||||
onUnload(() => {
|
||||
// 清理资源
|
||||
})
|
||||
|
||||
return {
|
||||
groupInfo,
|
||||
|
||||
@ -444,7 +444,7 @@ function getBubbleClass(message) {
|
||||
}
|
||||
|
||||
// 页面加载
|
||||
onLoad(async (options) => {
|
||||
onLoad((options) => {
|
||||
groupId.value = options.groupID || "";
|
||||
messageList.value = [];
|
||||
isLoading.value = false;
|
||||
@ -456,7 +456,7 @@ onLoad(async (options) => {
|
||||
if (options.userID) {
|
||||
chatInfo.value.userID = options.userID;
|
||||
}
|
||||
await getGroupInfo(groupId.value);
|
||||
|
||||
// 监听键盘高度变化
|
||||
uni.onKeyboardHeightChange((res) => {
|
||||
console.log("键盘高度变化:", res.height);
|
||||
@ -473,8 +473,6 @@ onLoad(async (options) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 先获取群组信息和成员信息,确保 chatMember 已初始化
|
||||
|
||||
checkLoginAndInitTIM();
|
||||
updateNavigationTitle();
|
||||
});
|
||||
@ -565,7 +563,7 @@ const initTIMCallbacks = async () => {
|
||||
.then(async () => {
|
||||
console.log("✓ 收到新消息后已标记为已读");
|
||||
// 标记为已读后,立即刷新 tabBar 徽章
|
||||
// await globalUnreadListenerManager.refreshBadge();
|
||||
await globalUnreadListenerManager.refreshBadge();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("✗ 标记已读失败:", error);
|
||||
@ -719,7 +717,7 @@ const loadMessageList = async () => {
|
||||
.then(async () => {
|
||||
console.log("✓ 会话已标记为已读:", chatInfo.value.conversationID);
|
||||
// 标记为已读后,立即刷新 tabBar 徽章
|
||||
// await globalUnreadListenerManager.refreshBadge();
|
||||
await globalUnreadListenerManager.refreshBadge();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("✗ 标记会话已读失败:", error);
|
||||
@ -915,10 +913,10 @@ onHide(() => {
|
||||
timChatManager.currentConversationID = null;
|
||||
console.log("✓ 页面隐藏,已清空当前会话ID");
|
||||
|
||||
// // 页面隐藏时刷新 tabBar 徽章,确保显示正确的未读数
|
||||
// if (globalUnreadListenerManager.isInitialized) {
|
||||
// globalUnreadListenerManager.refreshBadge();
|
||||
// }
|
||||
// 页面隐藏时刷新 tabBar 徽章,确保显示正确的未读数
|
||||
if (globalUnreadListenerManager.isInitialized) {
|
||||
globalUnreadListenerManager.refreshBadge();
|
||||
}
|
||||
});
|
||||
|
||||
// 处理取消申请
|
||||
|
||||
@ -380,8 +380,8 @@ const setupConversationListener = () => {
|
||||
// 将回调添加到全局未读监听器的回调链中
|
||||
// 这样不会覆盖全局未读监听器,而是与之共存
|
||||
if (globalUnreadListenerManager.isInitialized) {
|
||||
// globalUnreadListenerManager.addCallback("onConversationListUpdated", handleConversationListUpdate);
|
||||
// globalUnreadListenerManager.addCallback("onMessageReceived", handleMessageReceived);
|
||||
globalUnreadListenerManager.addCallback("onConversationListUpdated", handleConversationListUpdate);
|
||||
globalUnreadListenerManager.addCallback("onMessageReceived", handleMessageReceived);
|
||||
console.log("【消息列表页】已添加回调到全局监听器回调链");
|
||||
} else {
|
||||
console.warn("【消息列表页】全局未读监听器未初始化,使用直接回调方式");
|
||||
@ -454,7 +454,7 @@ const handleClickConversation = async (conversation) => {
|
||||
console.log("✓ 已标记会话为已读:", conversation.conversationID);
|
||||
|
||||
// 立即刷新 tabBar 徽章
|
||||
// await globalUnreadListenerManager.refreshBadge();
|
||||
await globalUnreadListenerManager.refreshBadge();
|
||||
} catch (error) {
|
||||
console.error("✗ 标记会话已读失败:", error);
|
||||
}
|
||||
@ -514,9 +514,9 @@ const cleanMessageText = (text) => {
|
||||
// 页面显示
|
||||
onShow(async () => {
|
||||
// 页面显示时刷新 tabBar 徽章
|
||||
// if (globalUnreadListenerManager.isInitialized) {
|
||||
// await globalUnreadListenerManager.refreshBadge();
|
||||
// }
|
||||
if (globalUnreadListenerManager.isInitialized) {
|
||||
await globalUnreadListenerManager.refreshBadge();
|
||||
}
|
||||
});
|
||||
|
||||
// 页面隐藏
|
||||
@ -544,12 +544,12 @@ onUnmounted(() => {
|
||||
updateTimer = null;
|
||||
}
|
||||
|
||||
// // 从全局未读监听器的回调链中移除本页面的回调
|
||||
// if (globalUnreadListenerManager.isInitialized) {
|
||||
// globalUnreadListenerManager.removeCallback("onConversationListUpdated", handleConversationListUpdate);
|
||||
// globalUnreadListenerManager.removeCallback("onMessageReceived", handleMessageReceived);
|
||||
// console.log("【消息列表页】已从回调链移除回调");
|
||||
// }
|
||||
// 从全局未读监听器的回调链中移除本页面的回调
|
||||
if (globalUnreadListenerManager.isInitialized) {
|
||||
globalUnreadListenerManager.removeCallback("onConversationListUpdated", handleConversationListUpdate);
|
||||
globalUnreadListenerManager.removeCallback("onMessageReceived", handleMessageReceived);
|
||||
console.log("【消息列表页】已从回调链移除回调");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -1,251 +0,0 @@
|
||||
<template>
|
||||
<full-page v-if="record">
|
||||
<view class="flex items-center px-15 py-10 border-b">
|
||||
<view>
|
||||
<image class="box-50" :src="record.avatar"></image>
|
||||
</view>
|
||||
<view class="ml-10">
|
||||
<view class="text-base font-semibold leading-normal">
|
||||
{{ record.userName || "" }}
|
||||
</view>
|
||||
<view class="text-sm text-gray">{{ record.job }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="px-15 py-10">
|
||||
<view class="text-base mb-10">请您对我的本次服务进行评价</view>
|
||||
<view class="flex items-center px-10 py-5 mb-10 border rounded">
|
||||
<view v-for="i in 5" :key="i" class="mr-5 w-star" @click="changeRate(i)">
|
||||
<uni-icons v-if="i <= starCount" type="star-filled" :size="30" color="#FF9900"></uni-icons>
|
||||
<uni-icons v-else type="star" :size="30" color="#FF9900"></uni-icons>
|
||||
</view>
|
||||
<view class="flex-shrink-0 w-star text-sm text-primary whitespace-nowrap">
|
||||
{{ rateText }}
|
||||
</view>
|
||||
</view>
|
||||
<template v-if="enable">
|
||||
<view class="flex flex-wrap text-sm">
|
||||
<view v-for="tag in tags" :key="tag._id"
|
||||
class="py-5 px-10 mr-10 mb-10 max-w-full break-all rounded-full bg-gray border" :class="selectedTag[tag._id]
|
||||
? 'text-white bg-primary border-primary'
|
||||
: ''
|
||||
" @click="toggle(tag)">
|
||||
{{ tag.text }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="px-10 py-5 border rounded">
|
||||
<textarea v-model="words" :auto-height="true" class="text-base block w-full min-h-100"
|
||||
placeholder="展开说说您对我本次服务的想法吧……" placeholder-class="text-gray text-base" :maxlength="1000" />
|
||||
<view class="text-gray text-right">
|
||||
{{ words.length }} / {{ 1000 }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view v-if="record.tags && record.tags.length" class="flex flex-wrap text-sm">
|
||||
<view v-for="(tag, i) in record.tags" :key="i"
|
||||
class="py-5 px-10 mr-10 mb-10 max-w-full break-all rounded-full text-white bg-primary border-primary border-[1px] border-solid">
|
||||
{{ tag }}
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="record.words && record.words.length"
|
||||
class="px-10 py-5 border border-solid border-gray-100 rounded pointer-events-none">
|
||||
<textarea :value="record.words" :disabled="true" class="text-sm block w-full min-h-[100px]"
|
||||
placeholder-class="text-gray text-sm" :maxlength="1000" />
|
||||
</view>
|
||||
</template>
|
||||
<view class="h-20"></view>
|
||||
</view>
|
||||
<template #footer>
|
||||
<view v-if="enable" class="flex-shrink-0 px-15 py-10 text-center bg-white">
|
||||
<view class="pb-10 text-sm">
|
||||
{{ record.userName ? `您正在对 ${record.userName} 进行匿名评价` : "您正在进行匿名评价" }}
|
||||
</view>
|
||||
<view class="py-10 text-base text-white rounded bg-primary" @click="confirm()">
|
||||
提交
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</full-page>
|
||||
<view v-else class="h-screen relative">
|
||||
<view v-if="isError" class="empty">
|
||||
<image class="empty__icon" src="/static/empty.svg"></image>
|
||||
<text class="empty__txt">{{ emptyTxt }} {{ record }}</text>
|
||||
</view>
|
||||
<view v-else-if="rated" class="empty text-green-500">
|
||||
<uni-icons type="checkbox-filled" color=" " size="60"></uni-icons>
|
||||
<text class="text-gray text-base block mt-4">评价已完成</text>
|
||||
</view>
|
||||
<view v-else class="empty text-gray">
|
||||
<!-- <view class="animate-spin ease-linear duration-[2500]">
|
||||
<uni-icons type="spinner-cycle animate-spin" color=" " size="50"></uni-icons>
|
||||
</view> -->
|
||||
<text class="text-base block mt-4">评价尚未完成</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import api from "@/utils/api.js";
|
||||
import { toast } from "@/utils/widget";
|
||||
|
||||
import FullPage from '@/components/full-page.vue';
|
||||
|
||||
|
||||
// import { submitRateRecord, getRateRecord } from "@/api/knowledgeBase.js";
|
||||
|
||||
const RateText = {
|
||||
1: "很不满意",
|
||||
2: "不满意",
|
||||
3: "一般",
|
||||
4: "满意",
|
||||
5: "很满意",
|
||||
};
|
||||
const RateStar = {
|
||||
1: "oneStar",
|
||||
2: "twoStar",
|
||||
3: "threeStar",
|
||||
4: "fourStar",
|
||||
5: "fiveStar",
|
||||
};
|
||||
|
||||
const id = ref("");
|
||||
const corpId = ref("");
|
||||
const isError = ref(true);
|
||||
const record = ref(null);
|
||||
const enable = ref(false);
|
||||
const rateTags = ref([]);
|
||||
const rated = ref(false);
|
||||
const emptyTxt = ref("正在获取评价信息...");
|
||||
const rate = ref(0);
|
||||
const words = ref("");
|
||||
const selectedTag = ref({});
|
||||
const loading = ref(false);
|
||||
|
||||
const tags = computed(() => {
|
||||
if (enable.value) {
|
||||
const key = RateStar[rate.value];
|
||||
const group = rateTags.value.find((i) => i.rateStar === key);
|
||||
return group && Array.isArray(group.rateTags) ? group.rateTags : [];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
const starCount = computed(() => {
|
||||
return rate.value % 1 === 0 && rate.value >= 0 && rate.value <= 5
|
||||
? rate.value
|
||||
: 0;
|
||||
})
|
||||
|
||||
const emptyStarCount = computed(() => {
|
||||
return 5 - starCount.value;
|
||||
});
|
||||
|
||||
const rateText = computed(() => {
|
||||
return RateText[starCount.value] || "";
|
||||
});
|
||||
|
||||
async function confirm() {
|
||||
if (!enable.value) return;
|
||||
if (![1, 2, 3, 4, 5].includes(rate.value)) {
|
||||
toast("请选择评分");
|
||||
return;
|
||||
}
|
||||
const tagsText = tags.value
|
||||
.filter((i) => selectedTag.value[i._id])
|
||||
.map((i) => i.text);
|
||||
loading.value = true;
|
||||
const { message, success } = await api('submitRateRecord', {
|
||||
id: id.value,
|
||||
corpId: corpId.value,
|
||||
rate: rate.value,
|
||||
tags: tagsText,
|
||||
words: words.value,
|
||||
});
|
||||
if (success) {
|
||||
await toast("评价成功");
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
toast(message);
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const res = await api('getRateRecord', {
|
||||
id: id.value,
|
||||
corpId: corpId.value,
|
||||
});
|
||||
if (res && res.success) {
|
||||
enable.value = typeof res.enable == 'boolean' ? res.enable : false;
|
||||
rated.value = typeof res.rated == 'boolean' ? res.rated : false;
|
||||
record.value = res.record;
|
||||
rate.value = res.record && res.record.rate ? res.record.rate : 0;
|
||||
rateTags.value = Array.isArray(res.rateTags) ? res.rateTags : [];
|
||||
if (record.value && record.value.updateTime) {
|
||||
words.value = typeof record.value.words == 'string' ? record.value.words : '';
|
||||
} else if (record.value && Date.now() > record.value.expireTime) {
|
||||
rate.value = 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function changeRate(i) {
|
||||
if (!enable.value) return;
|
||||
rate.value = i;
|
||||
selectedTag.value = {};
|
||||
}
|
||||
|
||||
function toggle(i) {
|
||||
if (!enable.value) return;
|
||||
selectedTag.value[i._id] = !selectedTag.value[i._id];
|
||||
}
|
||||
|
||||
onLoad(opts => {
|
||||
corpId.value = opts.corpId;
|
||||
id.value = opts.id;
|
||||
if (id.value && corpId.value) {
|
||||
init();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.h-screen {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.min-h-100 {
|
||||
min-height: 200rpx;
|
||||
}
|
||||
|
||||
.box-50 {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
|
||||
.w-star {
|
||||
width: 16%;
|
||||
}
|
||||
|
||||
.empty {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 40%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
|
||||
@at-root &__icon {
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
width: 240rpx;
|
||||
height: 240rpx;
|
||||
}
|
||||
|
||||
@at-root &__txt {
|
||||
font-size: 32rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,441 +0,0 @@
|
||||
<template>
|
||||
<full-page @reachBottom="getMore">
|
||||
<template #header>
|
||||
<scroll-view scroll-x class="bg-white whitespace-nowrap px-15 py-10 sticky top-0 z-10 w-full"
|
||||
:show-scrollbar="false">
|
||||
<view v-for="(tab, index) in tabs" :key="index"
|
||||
class="inline-block px-15 py-5 mr-10 text-sm rounded-full border transition-colors" :class="[
|
||||
activeTab === tab.value
|
||||
? 'bg-orange-100 text-orange-500 border-orange-500'
|
||||
: 'bg-white text-gray-600 border-gray-200'
|
||||
]" @click="selectTab(tab.value)">
|
||||
{{ tab.name }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
<view class="bg-gray-100 min-h-screen">
|
||||
<!-- Survey List -->
|
||||
<view v-if="loading && list.length === 0" class="loading-container">
|
||||
<uni-icons type="spinner-cycle" size="30" color="#999" />
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<view v-else-if="!loading && list.length === 0" class="empty-container">
|
||||
<empty-data text="暂无评价" />
|
||||
</view>
|
||||
|
||||
<view v-else class="p-15">
|
||||
<view v-for="item in list" :key="item._id" class="bg-white rounded-lg p-15 mb-15 shadow-sm"
|
||||
@click="goToDetail(item)">
|
||||
<!-- Header -->
|
||||
<view class="flex items-start justify-between mb-10">
|
||||
<view class="flex items-start flex-1 mr-10 relative">
|
||||
<view class="text-xs text-green-600 border border-green-600 px-5 rounded mr-5 flex-shrink-0 mt-1 tag-box">
|
||||
服务评价
|
||||
</view>
|
||||
<view class="text-base font-bold text-gray-800 leading-normal line-clamp-2">
|
||||
请您对我的今日服务做出评价
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="flex items-center flex-shrink-0 ml-2">
|
||||
<text v-if="item.rate" class="text-sm mr-2 text-gray-400">查看</text>
|
||||
<text v-else class="text-sm mr-2 text-danger">未评价</text>
|
||||
<uni-icons type="right" size="14" color="#9ca3af"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="text-sm text-gray-600 mb-5 flex items-start">
|
||||
<text class="text-gray-400 mr-2 field-label">服务人员:</text>
|
||||
<text>{{ item.userName || '-' }}</text>
|
||||
</view>
|
||||
|
||||
<view class="text-sm text-gray-600 mb-10 pb-10 border-b border-gray-100 flex items-start">
|
||||
<text class="text-gray-400 mr-2 field-label">团队:</text>
|
||||
<text>{{ item.teamName || '-' }}</text>
|
||||
</view>
|
||||
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="text-sm text-gray-400">
|
||||
服务时间: {{ item.time || '-' }}
|
||||
</view>
|
||||
<view class="text-sm text-gray-400">
|
||||
人员: {{ item.customerName || '-' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loading && list.length > 0" class="loading-more">
|
||||
<uni-icons type="spinner-cycle" size="20" color="#999" />
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
|
||||
<view v-if="!hasMore" class="no-more">
|
||||
没有更多了
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</full-page>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import { storeToRefs } from "pinia";
|
||||
import dayjs from "dayjs";
|
||||
import api from "@/utils/api.js";
|
||||
import usePageList from '@/hooks/usePageList';
|
||||
|
||||
import useAccountStore from "@/store/account.js";
|
||||
import EmptyData from "@/components/empty-data.vue";
|
||||
import FullPage from '@/components/full-page.vue';
|
||||
|
||||
const { openid } = storeToRefs(useAccountStore());
|
||||
|
||||
const tabs = ref([{ name: "全部", value: "" }]);
|
||||
const activeTab = ref("");
|
||||
const corpId = ref('')
|
||||
const teamId = ref('')
|
||||
|
||||
|
||||
const inited = ref(false);
|
||||
const { list, page, pages, pageSize, total, loading, hasMore, changePage } = usePageList(getList)
|
||||
|
||||
const selectTab = async (memberId) => {
|
||||
if (activeTab.value === memberId) return;
|
||||
activeTab.value = memberId;
|
||||
changePage(1)
|
||||
};
|
||||
|
||||
const loadCustomers = async () => {
|
||||
const miniAppId = openid.value || uni.getStorageSync("openid");
|
||||
if (!miniAppId) return;
|
||||
try {
|
||||
const res = await api("getMiniAppCustomers", { miniAppId, corpId: corpId.value });
|
||||
if (res && res.success) {
|
||||
const list = Array.isArray(res.data) ? res.data : [];
|
||||
tabs.value = [
|
||||
{ name: "全部", value: "" },
|
||||
...list.map((c) => ({ name: c.name || "未命名", value: c._id })),
|
||||
];
|
||||
} else {
|
||||
uni.showToast({ title: res?.message || "获取档案失败", icon: "none" });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("loadCustomers failed:", err);
|
||||
uni.showToast({ title: "获取档案失败", icon: "none" });
|
||||
}
|
||||
};
|
||||
|
||||
async function getList() {
|
||||
if (loading.value) return;
|
||||
const customerIds = activeTab.value ? [activeTab.value] : tabs.value.map(i => i.value);
|
||||
const res = await api('searchRateList', {
|
||||
corpId: corpId.value,
|
||||
page: page.value,
|
||||
pageSize: pageSize.value,
|
||||
customerIds,
|
||||
teamId: teamId.value
|
||||
})
|
||||
const arr = res && Array.isArray(res.list) ? res.list.map(i => ({
|
||||
...i,
|
||||
time: i.createTime ? dayjs(i.createTime).format('YYYY-MM-DD HH:mm') : '-',
|
||||
})) : [];
|
||||
list.value = page.value === 1 ? arr : [...list.value, ...arr];
|
||||
total.value = res && typeof res.total === 'number' ? res.total : 0;
|
||||
pages.value = res && typeof res.pages === 'number' ? res.pages : 0;
|
||||
};
|
||||
|
||||
function goToDetail(item) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/rate/rate-detail?id=${item._id}&corpId=${item.corpId}`
|
||||
})
|
||||
}
|
||||
|
||||
function getMore() {
|
||||
if (hasMore.value && !loading.value) {
|
||||
changePage(page.value + 1)
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(opts => {
|
||||
corpId.value = opts.corpId
|
||||
teamId.value = opts.teamId
|
||||
})
|
||||
|
||||
onShow(async () => {
|
||||
if (!inited.value) {
|
||||
await loadCustomers();
|
||||
inited.value = true
|
||||
}
|
||||
await changePage(1)
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.min-h-screen {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.bg-gray-100 {
|
||||
background-color: #f7f8fa;
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.p-15 {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.px-15 {
|
||||
padding-left: 30rpx;
|
||||
padding-right: 30rpx;
|
||||
}
|
||||
|
||||
.py-10 {
|
||||
padding-top: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.py-5 {
|
||||
padding-top: 10rpx;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.px-5 {
|
||||
padding-left: 10rpx;
|
||||
padding-right: 10rpx;
|
||||
}
|
||||
|
||||
.mr-10 {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.mr-5 {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.mb-15 {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.mt-1 {
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.pb-10 {
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.items-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.flex-shrink-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.border {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.border-b {
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
.rounded-full {
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.rounded-lg {
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.shadow-sm {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.text-xs {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.text-base {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.leading-normal {
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Colors - Adjusting to match image roughly */
|
||||
.text-orange-500 {
|
||||
color: #f29e38;
|
||||
}
|
||||
|
||||
.bg-orange-100 {
|
||||
background-color: #fff8eb;
|
||||
}
|
||||
|
||||
.border-orange-500 {
|
||||
border-color: #f29e38;
|
||||
}
|
||||
|
||||
.text-gray-600 {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.text-gray-500 {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.text-gray-400 {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.text-gray-800 {
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.border-gray-200 {
|
||||
border-color: #e5e5e5;
|
||||
}
|
||||
|
||||
.border-gray-100 {
|
||||
border-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.text-green-600 {
|
||||
color: #4b8d5f;
|
||||
}
|
||||
|
||||
.border-green-600 {
|
||||
border-color: #4b8d5f;
|
||||
}
|
||||
|
||||
.text-red-500 {
|
||||
color: #e04a4a;
|
||||
}
|
||||
|
||||
.sticky {
|
||||
position: sticky;
|
||||
}
|
||||
|
||||
.top-0 {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.z-10 {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.whitespace-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tag-box {
|
||||
border-radius: 4rpx;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.line-clamp-2 {
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.loading-container,
|
||||
.empty-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 0;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-top: 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.loading-more,
|
||||
.no-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 30rpx 0;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
gap: 10rpx;
|
||||
}
|
||||
</style>
|
||||
@ -1,53 +0,0 @@
|
||||
<template>
|
||||
<view class="h-full py-20vh">
|
||||
<view class="mb-10 px-15 text-lg font-semibold text-center">{{ survey.name }}</view>
|
||||
<scroll-view :scroll-y="true" class="h-40vh">
|
||||
<view class="text-sm text-gray px-15 text-center leading-normal">
|
||||
{{ survey.description || '' }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view v-if="customerName" class="my-4 text-center text-base">
|
||||
<text class="text-gray">客户:</text>
|
||||
<text class="font-semibold">{{ customerName }}</text>
|
||||
</view>
|
||||
<view
|
||||
class="mx-auto answer-btn h-12 text-base flex items-center justify-center text-white text-center rounded-full bg-primary"
|
||||
@click="answer()">
|
||||
开始答题
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
defineProps({
|
||||
customerName: { type: String, default: '' },
|
||||
survey: { type: Object, default: () => ({}) }
|
||||
})
|
||||
const emits = defineEmits(['answer'])
|
||||
|
||||
function answer() {
|
||||
emits('answer')
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>
|
||||
.h-40vh {
|
||||
height: 40vh;
|
||||
}
|
||||
|
||||
.py-20vh {
|
||||
padding: 20vh 0;
|
||||
}
|
||||
|
||||
.my-4 {
|
||||
margin-top: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.answer-btn {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.h-12 {
|
||||
height: 96rpx;
|
||||
}
|
||||
</style>
|
||||
@ -1,196 +0,0 @@
|
||||
<template>
|
||||
<view class="h-full flex flex-col">
|
||||
<view class="flex-shrink-0 px-10 pt-5 text-lg text-primary font-semibold text-center truncate">{{ survey.name }}
|
||||
</view>
|
||||
<view v-if="survey.description"
|
||||
class="flex-shrink-0 px-10 mt-10 text-sm leading-normal text-gray line-clamp-2 text-center">
|
||||
{{ survey.description }}
|
||||
</view>
|
||||
<view v-if="survey.enableScore" class="text-right mt-10 px-10 text-sm">
|
||||
当前得分:<span class="text-primary text-base font-semibold min-w-5 inline-block">{{ allScore }} </span>
|
||||
</view>
|
||||
<view v-if="quesiton" class="my-3 text-base font-semibold px-10 leading-normal ">
|
||||
<span v-if="quesiton.require" class="text-xs text-danger">*</span> {{ index + 1 }}、{{ quesiton.title }}
|
||||
</view>
|
||||
<view v-if="quesiton" class="flex-grow relative">
|
||||
<scroll-view :scroll-y="true" class="absolute inset-0">
|
||||
<view class="px-10">
|
||||
<template v-if="quesiton.type === 'radio'">
|
||||
<view v-for="(opt, idx) in quesiton.options" :key="opt.value" class="flex py-4 border-b "
|
||||
:class="idx === 0 ? 'border-t' : ''" @click="changeRadio(opt.value)">
|
||||
<uni-icons v-if="answer[quesiton.id] === opt.value" class="mr-2 text-primary flex-shrink-0" color=" "
|
||||
type="checkbox-filled" size="24"></uni-icons>
|
||||
<uni-icons v-else class="mr-2 text-gray flex-shrink-0" color=" " type="circle" size="24"></uni-icons>
|
||||
<view class="flex-grow text-base leading-normal">
|
||||
{{ opt.label }}
|
||||
<span v-if="survey.enableScore && opt.score >= 0">
|
||||
( {{ opt.score }}分)
|
||||
</span>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<textarea v-else-if="quesiton.type === 'input'" :value="answer[quesiton.id]" placeholder="请输入..."
|
||||
placeholder-class="text-gray text-sm"
|
||||
class="p-10 text-sm w-full box-border rounded border min-h-30 border-gray-200" @input="change($event)" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view v-if="list.length > 1" class="flex-shrink-0 py-3">
|
||||
<view class="mx-3 bg-gray-100 h-3 rounded-lg overflow-hidden">
|
||||
<view class="h-3 rounded-lg bg-primary" :style="{ width: `${progress}%` }"></view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between px-10 py-15">
|
||||
<view class="flex items-center text-primary" :class="index > 0 ? '' : 'opacity-0'" @click="prev()">
|
||||
<uni-icons class="inline-block mr-10rpx" color=" " type="arrowleft"></uni-icons>
|
||||
<span class="text-base font-semibold">上一题</span>
|
||||
</view>
|
||||
<!-- 当前得分:40 -->
|
||||
<view class="text-sm">{{ index + 1 }} / {{ list.length }}</view>
|
||||
<view v-if="index < list.length - 1" class="flex items-center text-primary"
|
||||
:class="index < list.length - 1 ? '' : 'opacity-0'" @click="next()">
|
||||
<span class="text-base font-semibold">下一题</span>
|
||||
<uni-icons class="inline-block ml-10rpx" color=" " type="arrowright"></uni-icons>
|
||||
</view>
|
||||
<view v-if="index === list.length - 1" class="flex items-center text-primary" @click="submit()">
|
||||
<span class="text-base font-semibold">提交</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="list.length === 1"
|
||||
class="flex-shrink-0 mx-10 py-12 leading-normal text-white text-center rounded bg-primary" @click="submit()">
|
||||
提交
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { toast } from '@/utils/widget'
|
||||
export default {
|
||||
name: 'Question',
|
||||
props: {
|
||||
list: { type: Array, default: () => ([]) },
|
||||
survey: { type: Object, default: () => ({}) }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
index: 0,
|
||||
answer: {},
|
||||
waiting: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
quesiton() {
|
||||
return this.list[this.index]
|
||||
},
|
||||
progress() {
|
||||
return (this.index + 1) / (this.list.length || 1) * 100
|
||||
},
|
||||
allScore() {
|
||||
return this.list.reduce((score, item) => {
|
||||
if (item.type === 'radio') {
|
||||
const opt = item.options.find(i => i.value && i.value === this.answer[item.id])
|
||||
if (opt && opt.score >= 0) {
|
||||
score = Math.floor(score * 100 + opt.score * 100) / 100
|
||||
}
|
||||
}
|
||||
return score
|
||||
}, 0)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
change(e) {
|
||||
this.$set(this.answer, this.quesiton.id, e.detail.value)
|
||||
},
|
||||
changeIndex(num) {
|
||||
if (this.waiting) return;
|
||||
this.waiting = true;
|
||||
setTimeout(() => this.waiting = false, 1000);
|
||||
const target = num + this.index;
|
||||
this.index = target >= 0 && target < this.list.length ? target : this.index
|
||||
},
|
||||
changeRadio(value) {
|
||||
this.$set(this.answer, this.quesiton.id, value)
|
||||
},
|
||||
prev() {
|
||||
this.changeIndex(-1)
|
||||
},
|
||||
next() {
|
||||
if (this.quesiton.require && this.quesiton.type === 'radio' && !this.quesiton.options.some(i => i.value && i.value === this.answer[this.quesiton.id])) {
|
||||
toast('请完成当前题目')
|
||||
} else if (this.quesiton.require && this.quesiton.type === 'input' && (typeof this.answer[this.quesiton.id] !== 'string' || this.answer[this.quesiton.id].trim() === '')) {
|
||||
toast('请完成当前题目')
|
||||
} else {
|
||||
this.changeIndex(1)
|
||||
}
|
||||
},
|
||||
verify() {
|
||||
const index = this.list.findIndex(i => {
|
||||
if (i.require && i.type === 'radio' && !i.options.some(j => j.value && j.value === this.answer[i.id])) {
|
||||
return true
|
||||
}
|
||||
if (i.require && i.type === 'input' && (typeof this.answer[i.id] !== 'string' || this.answer[i.id].trim() === '')) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
if (index >= 0) {
|
||||
toast('请完成第' + (index + 1) + '题')
|
||||
this.index = index
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
submit() {
|
||||
if (this.verify()) {
|
||||
const list = this.list.map(i => {
|
||||
const item = {
|
||||
id: i.id,
|
||||
value: this.answer[i.id] || '',
|
||||
title: i.title,
|
||||
type: i.type,
|
||||
require: i.require,
|
||||
}
|
||||
if (Array.isArray(i.options)) {
|
||||
item.options = i.options.map(opt => ({ ...opt }))
|
||||
}
|
||||
return item
|
||||
})
|
||||
this.$emit('submit', { list, score: this.allScore })
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.pt-5 {
|
||||
padding-top: 40rpx;
|
||||
}
|
||||
|
||||
.min-w-5 {
|
||||
min-width: 40rpx;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.my-3 {
|
||||
margin-top: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.py-4 {
|
||||
padding-top: 30rpx;
|
||||
padding-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.border-t {
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.min-h-30 {
|
||||
min-height: 200rpx;
|
||||
}
|
||||
</style>
|
||||
@ -1,93 +0,0 @@
|
||||
<template>
|
||||
<view class="h-full flex flex-col">
|
||||
<view class="flex-shrink-0 px-10 pt-5 text-base font-semibold text-center truncate">{{ surery.name }}</view>
|
||||
<view v-if="surery.description"
|
||||
class="flex-shrink-0 px-10 mt-10 text-sm leading-normal text-gray line-clamp-2 text-center">
|
||||
{{ surery.description }}
|
||||
</view>
|
||||
<view class="flex items-center text-gray text-sm px-10 mt-10">
|
||||
<view v-if="surery.enableScore">
|
||||
当前得分:<text class="text-primary text-base font-semibold min-w-5 inline-block">{{ allScore }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="ml-auto">
|
||||
客户:<text class="text-black text-base font-semibold min-w-5 inline-block">{{ customerName }} </text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-grow relative">
|
||||
<scroll-view :scroll-y="true" class="absolute inset-0">
|
||||
<view v-for="(quesiton, index) in list" :key="quesiton.id">
|
||||
<view v-if="quesiton" class="my-4 text-base px-10 leading-normal">
|
||||
<text v-if="quesiton.require" class="text-base text-red-500">*</text> {{ index + 1 }}、{{ quesiton.title }}
|
||||
</view>
|
||||
<view class="px-10">
|
||||
<template v-if="quesiton.type === 'radio'">
|
||||
<view v-for="(opt, idx) in quesiton.options" :key="opt.value" class="flex py-15 border-b"
|
||||
:class="idx === 0 ? 'border-t' : ''">
|
||||
<view class="flex-grow pl-4 text-gray leading-normal"
|
||||
:class="quesiton.value && quesiton.value === opt.value ? 'text-primary' : ''">
|
||||
{{ opt.label }}
|
||||
<text v-if="surery.enableScore && opt.score >= 0">
|
||||
( {{ opt.score }}分)
|
||||
</text>
|
||||
</view>
|
||||
<uni-icons v-if="quesiton.value && quesiton.value === opt.value" class="ml-1 text-primary flex-shrink-0"
|
||||
color=" " type="checkbox-filled" size="24"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
<view v-else-if="quesiton.type === 'input'"
|
||||
class="my-4 p-10 text-base leading-normal border rounded">
|
||||
{{ quesiton.value || '' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Question',
|
||||
props: {
|
||||
customerName: { type: String, default: '' },
|
||||
list: { type: Array, default: () => ([]) },
|
||||
surery: { type: Object, default: () => ({}) }
|
||||
},
|
||||
computed: {
|
||||
allScore() {
|
||||
return this.list.reduce((score, item) => {
|
||||
if (item.type === 'radio') {
|
||||
const opt = item.options.find(i => i.value && i.value === item.value && item.value)
|
||||
if (opt && opt.score >= 0) {
|
||||
score = Math.floor(score * 100 + opt.score * 100) / 100
|
||||
}
|
||||
}
|
||||
return score
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.min-w-5 {
|
||||
min-width: 40rpx
|
||||
}
|
||||
|
||||
.pt-5 {
|
||||
padding-top: 40rpx;
|
||||
}
|
||||
|
||||
.my-4 {
|
||||
margin-top: 32rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.border-t {
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.pl-4 {
|
||||
padding-left: 30rpx;
|
||||
}
|
||||
</style>
|
||||
@ -1,243 +0,0 @@
|
||||
<template>
|
||||
<full-page :customScroll="true">
|
||||
<template v-if="survey && survey.status === 'enable' && !readonly">
|
||||
<survey-cover v-if="step === 'cover'" :customerName="customerName" :survey="survey" @answer="step = 'question'" />
|
||||
<survey-question v-else-if="step === 'question'" :survey="survey" :list="showList" @submit="submit" />
|
||||
</template>
|
||||
<survey-record v-else-if="survey && survey.status === 'enable' && readonly" :customerName="customerName"
|
||||
:survey="survey" :list="showList" />
|
||||
<view v-else class="empty">
|
||||
<image class="empty__icon" src="/static/empty.svg"></image>
|
||||
<text class="empty__txt">{{ emptyTxt }}</text>
|
||||
</view>
|
||||
</full-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import api from "@/utils/api.js";
|
||||
import { loading, toast, hideLoading } from "@/utils/widget";
|
||||
|
||||
import FullPage from '@/components/full-page.vue';
|
||||
import surveyCover from "./components/survey-cover.vue";
|
||||
import surveyQuestion from "./components/survey-question.vue";
|
||||
import surveyRecord from "./components/survey-record.vue";
|
||||
|
||||
const corpId = ref('');
|
||||
const surveryId = ref('');
|
||||
const answerId = ref('');
|
||||
const memberId = ref('');
|
||||
const survey = ref(null);
|
||||
const emptyTxt = ref('')
|
||||
const customerName = ref('');
|
||||
const step = ref('cover');
|
||||
|
||||
const readonly = computed(() => survey.value && survey.value.submitTime);
|
||||
const list = computed(() => survey.value && Array.isArray(survey.value.list) ? survey.value.list : []);
|
||||
const showList = computed(() => {
|
||||
return list.value.filter(item => {
|
||||
const showCase = Array.isArray(item.showCase) ? item.showCase : [];
|
||||
if (showCase.length) {
|
||||
const res = showCase.every(({
|
||||
type,
|
||||
id,
|
||||
value
|
||||
}) => {
|
||||
const question = this.list.find(i => i.id === id);
|
||||
const val = question ? question.value : '';
|
||||
return type === 'in' ? value.includes(val) : val === value.join()
|
||||
})
|
||||
return res
|
||||
}
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
async function init() {
|
||||
loading('加载中...');
|
||||
try {
|
||||
const record = await getAnswerRecord();
|
||||
if (record && record.submitTime) {
|
||||
record.status = 'enable';
|
||||
survey.value = record;
|
||||
hideLoading()
|
||||
return;
|
||||
}
|
||||
survey.value = await getSurvey();
|
||||
if (survey.value.status == 'stop') {
|
||||
emptyTxt.value = '问卷已经停用'
|
||||
} else if (survey.value.status == 'init') {
|
||||
emptyTxt.value = '问卷暂未启用'
|
||||
}
|
||||
} catch (e) {
|
||||
hideLoading();
|
||||
toast('获取问卷失败');
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
|
||||
async function getAnswerRecord() {
|
||||
const res = await api('getAnswer', { corpId: corpId.value, surveryId: surveryId.value, answerId: answerId.value, memberId: memberId.value });
|
||||
if (res && res.success) {
|
||||
return res.record
|
||||
}
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
async function getSurvey() {
|
||||
const res = await api('getSurveryDetail', { corpId: corpId.value, id: surveryId.value })
|
||||
if (res && res.success && res.data) {
|
||||
return res.data
|
||||
}
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
async function submit({ list, score }) {
|
||||
const res = await api('answerSurvery', {
|
||||
corpId: corpId.value,
|
||||
name: survey.value.name,
|
||||
memberId: memberId.value,
|
||||
list: list,
|
||||
score: score,
|
||||
answerId: answerId.value,
|
||||
})
|
||||
if (res && res.success) {
|
||||
await toast('提交成功');
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(opts => {
|
||||
corpId.value = opts.corpId;
|
||||
surveryId.value = opts.surveryId;
|
||||
answerId.value = opts.answerId;
|
||||
memberId.value = opts.memberId;
|
||||
customerName.value = opts.name;
|
||||
init();
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.survey {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
color: ragb(0, 0, 0, .9);
|
||||
background: #fff;
|
||||
|
||||
@at-root &__wrapper {
|
||||
padding: 30rpx 40rpx;
|
||||
}
|
||||
|
||||
@at-root &__scroll {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@at-root &__customer {
|
||||
text-align: right;
|
||||
font-size: 28rpx;
|
||||
padding-bottom: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
color: #333;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
@at-root &__title {
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
@at-root &__desc {
|
||||
text-align: left;
|
||||
color: #666;
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
@at-root &__item {
|
||||
padding: 0 10rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
@at-root &__question {
|
||||
position: relative;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
@at-root &--require::before {
|
||||
position: absolute;
|
||||
left: -20rpx;
|
||||
top: 0;
|
||||
content: '*';
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
|
||||
@at-root &__input {
|
||||
padding: 10rpx 24rpx;
|
||||
font-size: 28rpx;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
@at-root &__radio {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 24rpx;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@at-root &__btn {
|
||||
margin-top: 30rpx;
|
||||
padding: 24rpx 30rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
background: #006eff;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.radio {
|
||||
@at-root &__icon {
|
||||
flex-shrink: 0;
|
||||
margin-top: 4rpx;
|
||||
margin-right: 10rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
@at-root &__label {
|
||||
flex-grow: 1;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 40%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
|
||||
@at-root &__icon {
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
width: 240rpx;
|
||||
height: 240rpx;
|
||||
}
|
||||
|
||||
@at-root &__txt {
|
||||
font-size: 32rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,18 +1,17 @@
|
||||
<template>
|
||||
<full-page pageClass="bg-gray-100">
|
||||
<template #header>
|
||||
<scroll-view scroll-x class="bg-white whitespace-nowrap px-15 py-10 sticky top-0 z-10 w-full"
|
||||
:show-scrollbar="false">
|
||||
<view v-for="(tab, index) in tabs" :key="index"
|
||||
class="inline-block px-15 py-5 mr-10 text-sm rounded-full border transition-colors" :class="[
|
||||
activeTab === tab.value
|
||||
? 'bg-orange-100 text-orange-500 border-orange-500'
|
||||
: 'bg-white text-gray-600 border-gray-200'
|
||||
]" @click="selectTab(tab.value)">
|
||||
{{ tab.name }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
<view class="bg-gray-100 min-h-screen">
|
||||
<!-- Filter Tabs -->
|
||||
<scroll-view scroll-x class="bg-white whitespace-nowrap px-15 py-10 sticky top-0 z-10 w-full"
|
||||
:show-scrollbar="false">
|
||||
<view v-for="(tab, index) in tabs" :key="index"
|
||||
class="inline-block px-15 py-5 mr-10 text-sm rounded-full border transition-colors" :class="[
|
||||
activeTab === tab.value
|
||||
? 'bg-orange-100 text-orange-500 border-orange-500'
|
||||
: 'bg-white text-gray-600 border-gray-200'
|
||||
]" @click="selectTab(tab.value)">
|
||||
{{ tab.name }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- Survey List -->
|
||||
<view v-if="loading && surveys.length === 0" class="loading-container">
|
||||
@ -42,8 +41,7 @@
|
||||
|
||||
<!-- Status (暂不展示未填写/已填写) -->
|
||||
<view class="flex items-center flex-shrink-0 ml-2">
|
||||
<text v-if="item.status === 'FILLED'" class="text-sm mr-2 text-gray-400">查看</text>
|
||||
<text v-else class="text-sm mr-2 text-danger">未填写</text>
|
||||
<text class="text-sm mr-2 text-gray-400">查看</text>
|
||||
<uni-icons type="right" size="14" color="#9ca3af"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
@ -74,7 +72,7 @@
|
||||
没有更多了
|
||||
</view>
|
||||
</view>
|
||||
</full-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -84,8 +82,6 @@ import { storeToRefs } from "pinia";
|
||||
import dayjs from "dayjs";
|
||||
import api from "@/utils/api.js";
|
||||
import useAccountStore from "@/store/account.js";
|
||||
|
||||
import fullPage from "@/components/full-page.vue";
|
||||
import EmptyData from "@/components/empty-data.vue";
|
||||
|
||||
|
||||
@ -93,8 +89,7 @@ const { openid } = storeToRefs(useAccountStore());
|
||||
|
||||
const tabs = ref([{ name: "全部", value: "" }]);
|
||||
const activeTab = ref("");
|
||||
const corpId = ref('');
|
||||
const teamId = ref('');
|
||||
const corpId = ref('')
|
||||
|
||||
const surveys = ref([]);
|
||||
const total = ref(0);
|
||||
@ -139,8 +134,6 @@ const mapRowToView = (row) => {
|
||||
team: row?.team?.name || "-",
|
||||
time: sendTime,
|
||||
status: row?.status || "",
|
||||
memberId: row?.memberId || "",
|
||||
corpId: row?.corpId || "",
|
||||
};
|
||||
};
|
||||
|
||||
@ -163,7 +156,6 @@ const loadSurveyList = async (reset = false) => {
|
||||
const params = {
|
||||
corpId: corpId.value,
|
||||
miniAppId,
|
||||
teamId: teamId.value,
|
||||
page: page.value,
|
||||
pageSize,
|
||||
};
|
||||
@ -187,17 +179,13 @@ const loadSurveyList = async (reset = false) => {
|
||||
}
|
||||
};
|
||||
|
||||
function goToDetail(item) {
|
||||
console.log(item)
|
||||
uni.navigateTo({
|
||||
url: `/pages/survey/fill?name=${item.person}&memberId=${item.memberId}&surveryId=${item.surveryId}&corpId=${item.corpId}&answerId=${item._id}`
|
||||
})
|
||||
function goToDetail() {
|
||||
uni.showToast({ title: "详情暂未接入", icon: "none" });
|
||||
}
|
||||
|
||||
|
||||
onLoad(opts => {
|
||||
corpId.value = opts.corpId;
|
||||
teamId.value = opts.teamId;
|
||||
corpId.value = opts.corpId
|
||||
})
|
||||
|
||||
onShow(async () => {
|
||||
|
||||
@ -11,9 +11,7 @@
|
||||
</view> -->
|
||||
<view class="flex">
|
||||
<view class="flex-shrink-0 text-base text-gray">执业机构:</view>
|
||||
<view v-if="member.source === 'wxapp'" class="flex-shrink-0 text-base text-dark">
|
||||
{{ member.hospitalName || '' }}
|
||||
</view>
|
||||
<view v-if="member.hospitalName" class="flex-shrink-0 text-base text-dark">{{ member.hospitalName }}</view>
|
||||
<view v-else class="flex-shrink-0 text-base text-dark">{{ corpNames }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -79,7 +79,7 @@ export default defineStore("accountStore", () => {
|
||||
console.log('IM 初始化成功');
|
||||
|
||||
// IM 初始化成功后,设置全局未读消息监听
|
||||
// globalUnreadListenerManager.setup();
|
||||
globalUnreadListenerManager.setup();
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
@ -99,7 +99,7 @@ export default defineStore("accountStore", () => {
|
||||
|
||||
// 清除全局未读监听
|
||||
if (globalUnreadListenerManager.isInitialized) {
|
||||
// globalUnreadListenerManager.destroy();
|
||||
globalUnreadListenerManager.destroy();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('退出腾讯IM失败:', error);
|
||||
|
||||
@ -40,10 +40,10 @@ export default defineStore("teamStore", () => {
|
||||
|
||||
// 获取团队成员头像和名称映射
|
||||
async function getTeamMemberAvatarsAndName(teamId) {
|
||||
|
||||
if (!teamId) return {};
|
||||
if (!teamId || !account.value?.corpId) return {};
|
||||
const res = await api('getTeamMemberAvatarsAndName', {
|
||||
teamId
|
||||
teamId,
|
||||
corpId: account.value.corpId
|
||||
}, false);
|
||||
if (res && res.success && res.data) {
|
||||
return res.data;
|
||||
|
||||
17
utils/api.js
17
utils/api.js
@ -12,9 +12,7 @@ const urlsConfig = {
|
||||
getCorpMemberJob: "getCorpMemberJob",
|
||||
bindWxappWithTeam: 'bindWxappWithTeam',
|
||||
getWxappRelateTeams: 'getWxappRelateTeams',
|
||||
getTeamMemberAvatarsAndName: "getTeamMemberAvatarsAndName",
|
||||
getMiniAppHomeStats: "getMiniAppHomeStats",
|
||||
getResponsiblePerson: 'getTeamResponsiblePerson'
|
||||
getTeamMemberAvatarsAndName: "getTeamMemberAvatarsAndName"
|
||||
},
|
||||
|
||||
knowledgeBase: {
|
||||
@ -38,10 +36,7 @@ const urlsConfig = {
|
||||
addArticleSendRecord: 'addArticleSendRecord',
|
||||
addArticleReadRecord: 'addArticleReadRecord',
|
||||
getMiniAppReceivedArticleList: 'getMiniAppReceivedArticleList',
|
||||
getPageDisease: 'getPageDisease',
|
||||
searchRateList: 'searchRateList',
|
||||
submitRateRecord: 'submitRateRecord',
|
||||
getRateRecord: 'getRateRecord',
|
||||
getPageDisease:'getPageDisease'
|
||||
},
|
||||
member: {
|
||||
addCustomer: 'add',
|
||||
@ -61,7 +56,7 @@ const urlsConfig = {
|
||||
updateCustomer: 'update'
|
||||
},
|
||||
wecom: {
|
||||
addContactWay: 'getCorpFriendQrcode'
|
||||
addContactWay: 'addContactWay'
|
||||
},
|
||||
im: {
|
||||
getUserSig: 'getUserSig',
|
||||
@ -75,11 +70,7 @@ const urlsConfig = {
|
||||
getGroupList: "getGroupList"
|
||||
},
|
||||
survery: {
|
||||
getMiniAppReceivedSurveryList: 'getMiniAppReceivedSurveryList',
|
||||
getSurveryDetail: 'getDetail',
|
||||
answerSurvery: 'answerSurvery',
|
||||
getAnswerRecord: 'getAnswerRecord',
|
||||
getAnswer: 'getAnswer'
|
||||
getMiniAppReceivedSurveryList: 'getMiniAppReceivedSurveryList'
|
||||
}
|
||||
}
|
||||
const urls = Object.keys(urlsConfig).reduce((acc, path) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user