Files
smartDriveEEUniApp/pages/furniture_reception/serviceListFurniture.vue

1142 lines
28 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<!-- 服务状态列表 -->
<scroll-view
class="service-status-list"
scroll-y
@scrolltolower="onServiceStatusReachBottom">
<view class="service-status-toolbar">
<text class="toolbar-total">{{ serviceStatusTotal }}</text>
<input
class="toolbar-input"
v-model="serviceStatusQuery.salesName"
placeholder="所属销售姓名"
placeholder-style="color: #b0b0b0"
confirm-type="search"
@confirm="onServiceStatusSearch"
/>
<input
class="toolbar-input"
v-model="serviceStatusQuery.customerName"
placeholder="客户姓名"
placeholder-style="color: #b0b0b0"
confirm-type="search"
@confirm="onServiceStatusSearch"
/>
<input
v-if="isAdmin"
class="toolbar-input"
v-model="serviceStatusQuery.salesPhone"
placeholder="销售电话"
placeholder-style="color: #b0b0b0"
confirm-type="search"
@confirm="onServiceStatusSearch"
/>
<view class="toolbar-actions">
<view class="toolbar-btn toolbar-btn--refresh" @click="onServiceStatusRefresh">
<uni-icons type="refresh" size="18" color="#2A68FF"></uni-icons>
<text>刷新</text>
</view>
</view>
</view>
<view
class="service-card"
v-for="(item, index) in serviceStatusList"
:key="item.id || index"
@click="viewServiceDetail(item)">
<view class="card-header">
<view class="staff-info">
<view class="staff-avatar">
<text class="avatar-text">{{ item.staffName ? item.staffName.charAt(0) : '未' }}</text>
</view>
<text class="staff-name">{{ item.staffName || '未分配销售' }}</text>
</view>
<view class="service-status">
<view class="service-action-btn service-action-btn--supplement" @click.stop="showSupplementDialog(item)">
<text>补录</text>
</view>
<view class="service-action-btn service-action-btn--finish" @click.stop="finishService(item)">
<text>结束</text>
</view>
<uni-icons type="bars" size="16" color="#007AFF"></uni-icons>
<text>服务中</text>
</view>
</view>
<view class="customer-info">
<text class="customer-name">客户{{ item.customerName || '未知客户' }}</text>
<text class="customer-phone" v-if="item.customerPhone">电话{{ item.customerPhone }}</text>
</view>
<view class="customer-tags">
<view
class="tag-item"
:class="'tag-' + tag.color"
v-for="(tag, tagIndex) in item.tags"
:key="tagIndex">
<text>{{ tag.text }}</text>
</view>
</view>
<!-- AI提醒 -->
<view class="ai-alert" v-if="item.alert" :class="item.alert.type === 'risk' ? 'alert-risk-box' : 'alert-reminder-box'">
<view class="alert-header">
<view class="alert-icon">
<view class="icon-circle"></view>
</view>
<text class="alert-title">{{ item.alert.title }}</text>
</view>
<view class="alert-content" :class="item.alert.type === 'risk' ? 'alert-risk-text' : 'alert-reminder-text'">
<text v-if="item.alert.type === 'risk'">{{ item.alert.message }}</text>
<view v-else class="alert-list">
<view class="alert-item" v-for="(msg, msgIndex) in item.alert.messages" :key="msgIndex">
<text>{{ msg }}</text>
</view>
</view>
</view>
</view>
<view class="service-duration">
<text>{{ item.durationText }}</text>
</view>
</view>
<view class="service-status-empty" v-if="!serviceStatusLoading && !serviceStatusList.length">
<text>暂无服务中记录</text>
</view>
<view class="service-status-loading-more" v-if="serviceStatusLoading && serviceStatusList.length">
<text>正在加载更多...</text>
</view>
</scroll-view>
<!-- 补录弹窗 -->
<view class="supplement-dialog-mask" v-if="showSupplementModal" @click="closeSupplementDialog">
<view class="supplement-dialog" @click.stop>
<view class="supplement-dialog__header">
<text class="supplement-dialog__title">补录信息</text>
<view class="supplement-dialog__close" @click="closeSupplementDialog">
<uni-icons type="close" size="20" color="#999"></uni-icons>
</view>
</view>
<view class="supplement-dialog__body">
<view class="supplement-form-item">
<text class="supplement-form-item__label">客户姓名</text>
<input
class="supplement-form-item__input"
v-model="supplementForm.customerName"
placeholder="请输入客户姓名"
placeholder-style="color: #9ca3af"
/>
</view>
<view class="supplement-form-item">
<text class="supplement-form-item__label">客户电话</text>
<input
class="supplement-form-item__input"
v-model="supplementForm.contact"
type="number"
placeholder="请输入客户电话"
placeholder-style="color: #9ca3af"
/>
</view>
<view class="supplement-form-item">
<text class="supplement-form-item__label">录音名</text>
<input
class="supplement-form-item__input"
v-model="supplementForm.recordingName"
placeholder="请输入录音名"
placeholder-style="color: #9ca3af"
/>
</view>
<view class="supplement-form-item">
<text class="supplement-form-item__label">客户详细地址</text>
<textarea
class="supplement-form-item__textarea"
v-model="supplementForm.remarks"
placeholder="请输入客户详细地址"
placeholder-style="color: #9ca3af"
:maxlength="500"
/>
</view>
</view>
<view class="supplement-dialog__footer">
<view class="supplement-dialog__btn supplement-dialog__btn--cancel" @click="closeSupplementDialog">
<text>取消</text>
</view>
<view class="supplement-dialog__btn supplement-dialog__btn--save" @click="saveSupplement">
<text>保存</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { getApiUrl } from "@/common/config.js";
export default {
data() {
return {
serviceStatusLoading: false,
serviceStatusTotal: 0,
serviceStatusPage: {
current: 1,
size: 10
},
serviceStatusQuery: {
salesName: '',
customerName: '',
salesPhone: ''
},
isAdmin: false,
currentUserPhone: '',
serviceStatusList: [],
// 补录相关
showSupplementModal: false,
currentSupplementItem: null,
supplementForm: {
customerName: "",
contact: "",
recordingName: "",
remarks: ""
}
}
},
mounted() {
// 检查用户角色
this.checkUserRole();
// 获取当前用户手机号
this.loadCurrentUserPhone();
this.fetchServiceStatusList();
},
methods: {
/**
* 检查用户角色判断是否包含admin
* 只要角色中包含admin字符串如admin_furniture就视为admin角色
*/
checkUserRole() {
try {
const normalize = (val) =>
typeof val === 'string'
? val
.split(',')
.map((s) => s.trim().toLowerCase())
.filter(Boolean)
: [];
const storedRole = uni.getStorageSync('backend-role-name') || '';
const loginResp = uni.getStorageSync('backend-login-response') || {};
const respRole = loginResp.roleName || '';
const roles = [...normalize(storedRole), ...normalize(respRole)];
// 检查角色数组中是否有任何角色包含 'admin' 字符串
this.isAdmin = roles.some(role => role.includes('admin'));
console.log('用户角色检查:', { storedRole, respRole, roles, isAdmin: this.isAdmin });
} catch (e) {
console.error('检查用户角色失败:', e);
this.isAdmin = false;
}
},
/**
* 加载当前用户手机号或登录账户
* 优先使用手机号,如果没有手机号则使用登录账户
*/
loadCurrentUserPhone() {
try {
const loginResponse = uni.getStorageSync('backend-login-response') || {};
// 优先使用手机号如果没有手机号则使用登录账户userName
if (loginResponse.phone) {
this.currentUserPhone = loginResponse.phone;
} else if (loginResponse.userName) {
this.currentUserPhone = loginResponse.userName;
} else {
this.currentUserPhone = '';
}
} catch (e) {
console.error('加载当前用户手机号或登录账户失败:', e);
this.currentUserPhone = '';
}
},
onServiceStatusSearch() {
this.serviceStatusPage.current = 1;
this.fetchServiceStatusList({ force: true });
},
onServiceStatusRefresh() {
this.serviceStatusPage.current = 1;
this.fetchServiceStatusList({ force: true });
},
onServiceStatusReachBottom() {
// 已在加载中或全部加载完成则不再触发
if (this.serviceStatusLoading) return;
if (this.serviceStatusList.length >= this.serviceStatusTotal) return;
this.serviceStatusPage.current += 1;
this.fetchServiceStatusList();
},
async fetchServiceStatusList({ force = false } = {}) {
if (this.serviceStatusLoading && !force) {
return;
}
this.serviceStatusLoading = true;
try {
// 构建查询参数对象
const queryParams = {
current: this.serviceStatusPage.current,
size: this.serviceStatusPage.size,
serviceStatus: '服务中' // 默认搜索条件
};
const trimmedSalesName = this.serviceStatusQuery?.salesName?.trim();
const trimmedCustomerName = this.serviceStatusQuery?.customerName?.trim();
if (trimmedSalesName) {
queryParams.salesName = trimmedSalesName;
}
if (trimmedCustomerName) {
queryParams.customerName = trimmedCustomerName;
}
// 根据角色处理销售电话查询条件
if (this.isAdmin) {
// 如果是admin使用搜索框中的销售电话
const trimmedSalesPhone = this.serviceStatusQuery?.salesPhone?.trim();
if (trimmedSalesPhone) {
queryParams.salesPhone = trimmedSalesPhone;
}
} else {
// 如果不是admin默认添加当前登录人的手机号或登录账户
if (this.currentUserPhone) {
queryParams.salesPhone = this.currentUserPhone;
}
}
// 确保 serviceStatus 始终存在
queryParams.serviceStatus = '服务中';
// 将参数转换为 URL 查询字符串(因为后端使用 @RequestParam
const queryString = Object.keys(queryParams)
.filter(key => queryParams[key] !== null && queryParams[key] !== undefined && queryParams[key] !== '')
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`)
.join('&');
// 调试:打印请求参数
console.log('服务状态查询参数:', JSON.stringify(queryParams));
console.log('查询字符串:', queryString);
// 后端使用 @RequestParam参数需要作为 URL 查询参数传递
const baseUrl = getApiUrl('/api/audioManagement/list');
const url = queryString ? `${baseUrl}?${queryString}` : baseUrl;
// 获取认证信息
let tenantId = '';
let token = '';
try {
tenantId = uni.getStorageSync('backend-tenant-id') || '';
token = uni.getStorageSync('backend-token') || '';
} catch (e) {
console.error('获取认证信息失败:', e);
}
// 构建请求头
const headers = {
'Content-Type': 'application/json'
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
if (tenantId) {
headers['X-Tenant-Id'] = tenantId;
}
const res = await uni.request({
url,
method: 'POST',
data: {}, // POST 请求体为空,参数都在 URL 查询字符串中
header: headers,
timeout: 30000 // 增加超时时间到30秒
});
if (res.statusCode === 200 && res.data && res.data.success) {
const records = Array.isArray(res.data.data) ? res.data.data : [];
if (records.length === 0) {
this.serviceStatusList = [];
this.serviceStatusTotal = 0;
return;
}
const mapped = records
.map(item => this.buildServiceStatusItem(item))
.filter(item => item !== null); // 过滤掉无效的数据
// 第一页或强制刷新时重置列表,否则追加
if (this.serviceStatusPage.current === 1 || force) {
// 使用 Vue 3 的响应式更新方式
this.serviceStatusList = [...mapped];
} else {
this.serviceStatusList = [...this.serviceStatusList, ...mapped];
}
this.serviceStatusTotal = Number(res.data.total) || 0;
} else {
this.serviceStatusList = [];
this.serviceStatusTotal = 0;
uni.showToast({
title: res.data?.message || '获取服务中列表失败',
icon: 'none'
});
}
} catch (error) {
console.error('获取服务中列表失败:', error);
// 根据错误类型提供更详细的错误信息
let errorMessage = '获取服务状态失败,请稍后重试';
if (error.errMsg) {
if (error.errMsg.includes('timeout')) {
errorMessage = '请求超时,请检查网络连接后重试';
} else if (error.errMsg.includes('fail')) {
errorMessage = '网络请求失败,请检查网络连接';
}
}
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 3000
});
} finally {
this.serviceStatusLoading = false;
}
},
buildServiceStatusItem(record = {}) {
if (!record || typeof record !== 'object') {
return null;
}
const formattedUpdateTime = this.formatDateTime(record.updateTime || record.createTime);
const tags = [];
if (record.recordingName) {
tags.push({ text: `录音:${record.recordingName}`, color: 'blue' });
}
if (record.intentionLevel) {
tags.push({ text: `意向:${record.intentionLevel}`, color: 'orange' });
}
if (record.projectName) {
tags.push({ text: `项目:${record.projectName}`, color: 'blue' });
}
return {
id: record.id || '',
staffName: record.salesName || '未分配销售',
status: record.syncStatus || '服务中',
customerName: record.customerName || '',
customerPhone: record.customerPhone || '',
customerId: record.customerId || '',
recordingName: record.recordingName || '',
remarks: record.remarks || '',
tags,
durationText: formattedUpdateTime ? `最近更新时间:${formattedUpdateTime}` : '最近暂无更新时间'
};
},
formatDateTime(value) {
if (!value) {
return '';
}
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
return '';
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},
viewServiceDetail(item) {
// 查看服务详情
console.log('查看服务详情', item);
},
async finishService(item) {
if (!item || !item.id) {
uni.showToast({
title: '无法获取服务记录ID',
icon: 'none'
});
return;
}
uni.showModal({
title: '确认结束',
content: `确定要结束这条服务记录吗?`,
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({
title: '结束中...'
});
const url = getApiUrl('/api/audioManagement/finishServiceById');
// 获取认证信息
let tenantId = '';
let token = '';
try {
tenantId = uni.getStorageSync('backend-tenant-id') || '';
token = uni.getStorageSync('backend-token') || '';
} catch (e) {
console.error('获取认证信息失败:', e);
}
// 构建请求头
const headers = {
'Content-Type': 'application/json'
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
if (tenantId) {
headers['X-Tenant-Id'] = tenantId;
}
// 构建请求体
const requestData = {
id: item.id
};
const finishRes = await uni.request({
url: url,
method: 'POST',
data: requestData,
header: headers,
timeout: 30000
});
uni.hideLoading();
if (finishRes.statusCode === 200 && finishRes.data && finishRes.data.success) {
uni.showToast({
title: finishRes.data?.message || '结束服务成功',
icon: 'success'
});
// 结束成功后刷新列表
this.serviceStatusPage.current = 1;
this.fetchServiceStatusList({ force: true });
} else {
uni.showToast({
title: finishRes.data?.message || '结束服务失败',
icon: 'none'
});
}
} catch (error) {
uni.hideLoading();
console.error('结束服务失败:', error);
// 根据错误类型提供更详细的错误信息
let errorMessage = '结束服务失败,请重试';
if (error.errMsg) {
if (error.errMsg.includes('timeout')) {
errorMessage = '请求超时,请检查网络连接后重试';
} else if (error.errMsg.includes('fail')) {
errorMessage = '网络请求失败,请检查网络连接';
}
}
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 3000
});
}
}
}
});
},
showSupplementDialog(item) {
this.currentSupplementItem = item;
// 初始化表单数据,如果有现有数据则填充
this.supplementForm = {
customerName: item.customerName || "",
contact: item.customerPhone || "",
recordingName: item.recordingName || "",
remarks: item.remarks || ""
};
this.showSupplementModal = true;
},
closeSupplementDialog() {
this.showSupplementModal = false;
this.currentSupplementItem = null;
this.supplementForm = {
customerName: "",
contact: "",
recordingName: "",
remarks: ""
};
},
async saveSupplement() {
// 验证电话格式(如果填写了电话)
const trimmedContact = this.supplementForm.contact?.trim();
if (trimmedContact && !this.isValidPhoneNumber(trimmedContact)) {
uni.showToast({
title: "请输入有效的客户电话",
icon: "none"
});
return;
}
if (!this.currentSupplementItem || !this.currentSupplementItem.id) {
uni.showToast({
title: '无法获取服务记录ID',
icon: 'none'
});
return;
}
try {
uni.showLoading({
title: "保存中..."
});
// 获取认证信息
let tenantId = '';
let token = '';
try {
tenantId = uni.getStorageSync('backend-tenant-id') || '';
token = uni.getStorageSync('backend-token') || '';
} catch (e) {
console.error('获取认证信息失败:', e);
}
// 构建请求头
const headers = {
'Content-Type': 'application/json'
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
if (tenantId) {
headers['X-Tenant-Id'] = tenantId;
}
// 构建请求参数必须包含id
const params = {
id: this.currentSupplementItem.id,
customerId: this.currentSupplementItem.customerId || "",
customerName: this.supplementForm.customerName?.trim() || "",
customerPhone: trimmedContact || "",
recordingName: this.supplementForm.recordingName?.trim() || "",
remarks: this.supplementForm.remarks?.trim() || ""
};
// 调用补录接口
const res = await uni.request({
url: getApiUrl('/api/audioManagement/updateForCustomerInfo'),
method: "PUT",
data: params,
header: headers,
timeout: 30000
});
uni.hideLoading();
if (res.statusCode === 200 && res.data && res.data.success) {
uni.showToast({
title: "补录成功",
icon: "success"
});
// 关闭弹窗
this.closeSupplementDialog();
// 刷新列表
this.serviceStatusPage.current = 1;
this.fetchServiceStatusList({ force: true });
} else {
uni.showToast({
title: res.data?.message || "补录失败",
icon: "none"
});
}
} catch (error) {
uni.hideLoading();
console.error("补录失败:", error);
let errorMessage = '补录失败,请重试';
if (error.errMsg) {
if (error.errMsg.includes('timeout')) {
errorMessage = '请求超时,请检查网络连接后重试';
} else if (error.errMsg.includes('fail')) {
errorMessage = '网络请求失败,请检查网络连接';
}
}
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 3000
});
}
},
isValidPhoneNumber(phone) {
const normalizedPhone = phone?.trim();
if (!normalizedPhone) {
return false;
}
return /^1[3-9]\d{9}$/.test(normalizedPhone);
}
}
}
</script>
<style scoped>
/* 最外层容器 */
.service-status-list {
height: 100%;
width: 100%;
background-color: #F5F5F5;
padding: 24rpx 16rpx 0 16rpx;
box-sizing: border-box;
}
.service-status-toolbar {
display: flex;
flex-wrap: nowrap;
align-items: center;
gap: 8rpx;
padding: 12rpx 8rpx;
margin-bottom: 24rpx;
background-color: #F8F8FA;
border-radius: 8rpx;
border: 1px solid #EFEFF2;
overflow-x: auto;
min-height: 64rpx;
box-sizing: border-box;
}
.toolbar-total {
font-size: 24rpx;
color: #666;
white-space: nowrap;
flex-shrink: 0;
margin-right: 8rpx;
line-height: 1.2;
padding: 0 4rpx;
}
.toolbar-input {
flex: 1;
min-width: 120rpx;
max-width: 200rpx;
height: 56rpx;
line-height: 56rpx;
background-color: #F5F6FA;
border-radius: 8rpx;
padding: 0 12rpx;
font-size: 24rpx;
border: 1px solid transparent;
box-sizing: border-box;
flex-shrink: 1;
}
.toolbar-actions {
display: flex;
align-items: center;
margin-left: auto;
flex-shrink: 0;
gap: 8rpx;
}
.toolbar-btn {
padding: 0 12rpx;
height: 56rpx;
min-width: 56rpx;
color: #2A68FF;
display: flex;
align-items: center;
justify-content: center;
gap: 4rpx;
font-size: 24rpx;
font-weight: 400;
line-height: 1;
box-sizing: border-box;
}
.service-card {
width: 100%;
box-sizing: border-box;
background-color: #FFFFFF;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
overflow: visible;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.staff-info {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
margin: 0;
padding: 0;
}
.staff-avatar {
width: 64rpx;
height: 64rpx;
background-color: #2196F3;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16rpx;
margin-left: 0;
margin-top: 0;
margin-bottom: 0;
flex-shrink: 0;
}
.avatar-text {
font-size: 32rpx;
color: #FFFFFF;
font-weight: 500;
}
.staff-name {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.service-status {
display: flex;
align-items: center;
gap: 8rpx;
}
.service-status text {
font-size: 26rpx;
color: #007AFF;
}
.customer-tags {
display: flex;
flex-wrap: wrap;
margin-bottom: 20rpx;
gap: 12rpx;
box-sizing: border-box;
margin-left: 0;
margin-right: 0;
padding: 0;
width: 100%;
max-width: 100%;
}
.customer-info {
margin-bottom: 16rpx;
display: flex;
flex-direction: column;
gap: 8rpx;
box-sizing: border-box;
margin-left: 0;
margin-right: 0;
padding: 0;
width: 100%;
max-width: 100%;
}
.customer-name,
.customer-phone {
font-size: 28rpx;
color: #555;
}
.tag-item {
padding: 8rpx 16rpx;
border-radius: 8rpx;
box-sizing: border-box;
word-wrap: break-word;
word-break: break-all;
display: inline-block;
max-width: 100%;
}
.tag-blue {
background-color: #E3F2FD;
}
.tag-blue text {
font-size: 24rpx;
color: #1976D2;
}
.tag-orange {
background-color: #FFF3E0;
}
.tag-orange text {
font-size: 24rpx;
color: #F57C00;
}
/* AI提醒 */
.ai-alert {
border-radius: 8rpx;
padding: 20rpx;
margin-bottom: 20rpx;
}
.alert-risk-box {
background-color: #FFF5F5;
}
.alert-reminder-box {
background-color: #F5F5F5;
}
.alert-header {
display: flex;
align-items: center;
margin-bottom: 12rpx;
}
.alert-icon {
margin-right: 8rpx;
}
.icon-circle {
width: 24rpx;
height: 24rpx;
border: 2rpx solid #333;
border-radius: 50%;
position: relative;
}
.icon-circle::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 8rpx;
height: 8rpx;
background-color: #333;
border-radius: 50%;
}
.alert-title {
font-size: 28rpx;
font-weight: 500;
color: #333;
}
.alert-content {
font-size: 26rpx;
line-height: 1.6;
}
.alert-risk-text {
color: #FF5722;
}
.alert-reminder-text {
color: #666;
}
.alert-list {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.alert-item {
display: flex;
align-items: flex-start;
}
.alert-item::before {
content: '•';
margin-right: 8rpx;
color: #666;
}
.alert-item text {
font-size: 26rpx;
color: #666;
line-height: 1.6;
}
.service-duration {
padding-top: 16rpx;
border-top: 1px solid #F0F0F0;
}
.service-duration text {
font-size: 26rpx;
color: #999;
}
.service-action-btn {
padding: 6rpx 16rpx;
border-radius: 4rpx;
font-size: 26rpx;
font-weight: 400;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12rpx;
cursor: pointer;
}
.service-action-btn--finish {
color: #007AFF;
background-color: transparent;
}
.service-action-btn--finish:active {
opacity: 0.7;
}
.service-action-btn--supplement {
color: #10B981;
background-color: transparent;
}
.service-action-btn--supplement:active {
opacity: 0.7;
}
.service-status-empty {
padding: 48rpx 0;
text-align: center;
color: #999;
font-size: 28rpx;
}
.service-status-loading-more {
padding: 48rpx 0;
text-align: center;
color: #999;
font-size: 28rpx;
}
/* 补录弹窗样式 */
.supplement-dialog-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.supplement-dialog {
width: 640rpx;
max-height: 80vh;
background-color: #FFFFFF;
border-radius: 24rpx;
overflow: hidden;
display: flex;
flex-direction: column;
}
.supplement-dialog__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx 32rpx 24rpx 32rpx;
border-bottom: 1px solid #F0F0F0;
}
.supplement-dialog__title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.supplement-dialog__close {
width: 48rpx;
height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
}
.supplement-dialog__body {
flex: 1;
padding: 32rpx;
overflow-y: auto;
}
.supplement-form-item {
margin-bottom: 32rpx;
}
.supplement-form-item:last-child {
margin-bottom: 0;
}
.supplement-form-item__label {
display: block;
font-size: 28rpx;
color: #333;
font-weight: 500;
margin-bottom: 16rpx;
}
.supplement-form-item__input {
width: 100%;
height: 88rpx;
background-color: #f9fafb;
border-radius: 12rpx;
padding: 0 24rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
}
.supplement-form-item__textarea {
width: 100%;
min-height: 160rpx;
background-color: #f9fafb;
border-radius: 12rpx;
padding: 24rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
line-height: 1.6;
}
.supplement-dialog__footer {
display: flex;
gap: 24rpx;
padding: 24rpx 32rpx 32rpx 32rpx;
border-top: 1px solid #F0F0F0;
}
.supplement-dialog__btn {
flex: 1;
height: 88rpx;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 500;
}
.supplement-dialog__btn--cancel {
background-color: #f3f4f6;
color: #6b7280;
}
.supplement-dialog__btn--save {
background-color: #4C8DFF;
color: #ffffff;
}
.supplement-dialog__btn:active {
opacity: 0.7;
}
</style>