Browse Source

修改服务申报

master
wang 3 months ago
parent
commit
b1b2583b1b
  1. BIN
      dist.zip
  2. 4
      package-lock.json
  3. 4
      src/api/business/service-applications/service-applications-api.js
  4. 2
      src/components/business/category-tree-select/index.vue
  5. 2
      src/views/business/erp/cost/firm-reports-list.vue
  6. 18
      src/views/business/erp/goods/components/goods-form-modal.vue
  7. 14
      src/views/business/erp/goods/goods-list.vue
  8. 295
      src/views/business/erp/service/service-applications-form.vue
  9. 15
      src/views/business/erp/service/service-applications-list.vue
  10. 361
      src/views/mobile/components/agreement-modal.vue
  11. 6
      src/views/mobile/index.vue
  12. 6
      src/views/mobile/login.vue
  13. 1042
      src/views/mobile/service/create.vue
  14. 597
      src/views/mobile/service/detail.vue
  15. 12
      vite.config.js

BIN
dist.zip

Binary file not shown.

4
package-lock.json

@ -1,11 +1,11 @@
{ {
"name": "smartadmin", "name": "yun-admin",
"version": "3.0.0", "version": "3.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "smartadmin", "name": "yun-admin",
"version": "3.0.0", "version": "3.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

4
src/api/business/service-applications/service-applications-api.js

@ -32,8 +32,8 @@ export const serviceApplicationsApi = {
/** /**
* 编辑提交 @author wzh * 编辑提交 @author wzh
*/ */
submit: (id) => { submit: (param) => {
return getRequest(`/serviceApplications/submit/${id}`); return postRequest('/serviceApplications/submit', param);
}, },
/** /**
* 审核 @author wzh * 审核 @author wzh

2
src/components/business/category-tree-select/index.vue

@ -7,7 +7,7 @@
<a-tree-select <a-tree-select
v-model:value="selectValue" v-model:value="selectValue"
:style="`width:${width}`" :style="`width:${width}`"
:dropdown-style="{ maxHeight: '400px', overflowX: 'auto', maxWidth: '350px' }" :dropdown-style="{ maxWidth: '350px' }"
:tree-data="categoryTreeData" :tree-data="categoryTreeData"
:placeholder="placeholder" :placeholder="placeholder"
:allowClear="true" :allowClear="true"

2
src/views/business/erp/cost/firm-reports-list.vue

@ -10,7 +10,7 @@
<a-form class="smart-query-form"> <a-form class="smart-query-form">
<a-row class="smart-query-form-row"> <a-row class="smart-query-form-row">
<a-form-item label="律师事务所" class="smart-query-form-item"> <a-form-item label="律师事务所" class="smart-query-form-item">
<DepartmentTreeSelect style="width: 150px" v-model:value="queryForm.firmId" placeholder="请选择律师事务所" /> <DepartmentTreeSelect style="width: 250px" v-model:value="queryForm.firmId" placeholder="请选择律师事务所" />
</a-form-item> </a-form-item>
<a-form-item label="报表年份" class="smart-query-form-item"> <a-form-item label="报表年份" class="smart-query-form-item">
<a-input style="width: 150px" v-model:value="queryForm.declareYear" placeholder="请输入报表年份" /> <a-input style="width: 150px" v-model:value="queryForm.declareYear" placeholder="请输入报表年份" />

18
src/views/business/erp/goods/components/goods-form-modal.vue

@ -17,7 +17,15 @@
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item label="小时(次)" name="price"> <a-form-item label="小时(次)" name="price">
<a-input-number style="width: 100%" placeholder="请输入小时(次)" v-model:value="form.price" :min="0" /> <a-input style="width: 100%" placeholder="请输入小时(次)" v-model:value="form.price" :min="0" />
</a-form-item>
<a-form-item label="计算类型" name="timeType" >
<DictSelect
v-model:value="form.timeType"
placeholder="请选择计算类型"
dictCode="COMPUTATIONTYPE"
style="width: 100%"
/>
</a-form-item> </a-form-item>
<a-form-item label="备注" name="remark"> <a-form-item label="备注" name="remark">
<a-input style="width: 100%" placeholder="请输入备注" v-model:value="form.remark" /> <a-input style="width: 100%" placeholder="请输入备注" v-model:value="form.remark" />
@ -42,7 +50,7 @@
</a-drawer> </a-drawer>
</template> </template>
<script setup> <script setup>
import { ref, nextTick, reactive } from 'vue'; import { ref, nextTick, reactive, onMounted } from 'vue';
import CategoryTree from '/@/components/business/category-tree-select/index.vue'; import CategoryTree from '/@/components/business/category-tree-select/index.vue';
import { CATEGORY_TYPE_ENUM } from '/@/constants/business/erp/category-const'; import { CATEGORY_TYPE_ENUM } from '/@/constants/business/erp/category-const';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
@ -54,6 +62,7 @@
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue'; import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
import DictSelect from '/@/components/support/dict-select/index.vue'; import DictSelect from '/@/components/support/dict-select/index.vue';
import { DICT_CODE_ENUM } from '/@/constants/support/dict-const'; import { DICT_CODE_ENUM } from '/@/constants/support/dict-const';
import { useDictStore } from '/@/store/modules/system/dict.js';
// emit // emit
const emit = defineEmits(['reloadList']); const emit = defineEmits(['reloadList']);
@ -70,6 +79,8 @@
price: undefined, price: undefined,
// //
shelvesFlag: true, shelvesFlag: true,
//
timeType: undefined,
// //
remark: '', remark: '',
//id //id
@ -79,7 +90,6 @@
const rules = { const rules = {
categoryId: [{ required: true, message: '请选择活动分类' }], categoryId: [{ required: true, message: '请选择活动分类' }],
goodsName: [{ required: true, message: '活动名称不能为空' }], goodsName: [{ required: true, message: '活动名称不能为空' }],
price: [{ required: true, message: '活动小时(1次)不能为空' }],
}; };
// //
const visible = ref(false); const visible = ref(false);
@ -103,6 +113,8 @@
visible.value = false; visible.value = false;
} }
function onSubmit() { function onSubmit() {
formRef.value formRef.value
.validate() .validate()

14
src/views/business/erp/goods/goods-list.vue

@ -7,7 +7,7 @@
<a-row class="smart-query-form-row" v-privilege="'goods:query'"> <a-row class="smart-query-form-row" v-privilege="'goods:query'">
<a-form-item label="活动分类" class="smart-query-form-item"> <a-form-item label="活动分类" class="smart-query-form-item">
<category-tree <category-tree
width="250px" width="300px"
v-model:value="queryForm.categoryId" v-model:value="queryForm.categoryId"
placeholder="请选择活动分类" placeholder="请选择活动分类"
:categoryType="CATEGORY_TYPE_ENUM.GOODS.value" :categoryType="CATEGORY_TYPE_ENUM.GOODS.value"
@ -15,7 +15,7 @@
/> />
</a-form-item> </a-form-item>
<a-form-item style="margin-left:100px;width: 300px" label="活动名称" class="smart-query-form-item"> <a-form-item style="margin-left:150px;width: 300px" label="活动名称" class="smart-query-form-item">
<a-input v-model:value="queryForm.searchWord" placeholder="活动名称" /> <a-input v-model:value="queryForm.searchWord" placeholder="活动名称" />
</a-form-item> </a-form-item>
@ -202,7 +202,7 @@
</style> </style>
<script setup> <script setup>
import GoodsFormModal from './components/goods-form-modal.vue'; import GoodsFormModal from './components/goods-form-modal.vue';
import { onMounted, reactive, ref } from 'vue'; import { onMounted, reactive, ref, h } from 'vue';
import { message, Modal } from 'ant-design-vue'; import { message, Modal } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading'; import { SmartLoading } from '/@/components/framework/smart-loading';
import { goodsApi } from '/@/api/business/goods/goods-api'; import { goodsApi } from '/@/api/business/goods/goods-api';
@ -267,6 +267,14 @@
resizable: true, resizable: true,
width: 150, width: 150,
}, },
{
title: '计算类型',
dataIndex: 'timeType',
ellipsis: true,
resizable: true,
width: 100,
customRender: ({ text }) => h(DictLabel, { dictCode: 'COMPUTATIONTYPE', dataValue: text }),
},
{ {
title: '创建时间', title: '创建时间',
dataIndex: 'createTime', dataIndex: 'createTime',

295
src/views/business/erp/service/service-applications-form.vue

@ -57,7 +57,39 @@
</a-col> </a-col>
</a-row> </a-row>
<!-- 服务时间信息 -->
<!-- 活动信息 -->
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="活动类型" name="activityCategoryId">
<CategoryTree
v-model:value="form.activityCategoryId"
:category-type="CATEGORY_TYPE_ENUM.GOODS.value"
placeholder="请选择活动类型"
style="width: 100%"
:disabled="readonlyMode"
@change="onActivityCategoryChange"
/>
</a-form-item>
</a-col>
<a-col :span="16">
<a-form-item label="活动名称" name="activityNameId">
<a-select
v-model:value="form.activityNameId"
placeholder="请选择活动名称"
style="width: 100%"
:disabled="!form.activityCategoryId || readonlyMode"
:options="activityList"
show-search
:filter-option="filterActivityOption"
/>
</a-form-item>
</a-col>
</a-row>
<!-- 根据活动类型显示不同字段 -->
<template v-if="currentActivityType === 'DICT' || !currentActivityType">
<!-- dict类型显示服务时长服务开始时间和服务结束时间 -->
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :span="8"> <a-col :span="8">
<a-form-item label="服务开始时间" name="serviceStart" label-align="left"> <a-form-item label="服务开始时间" name="serviceStart" label-align="left">
@ -80,36 +112,54 @@
<div style="font-size: 12px; color: #f00d0dff; margin-top: -12px; margin-bottom: 12px;">:不足30分钟不含本数不计入时长超过30分钟不足1个小时的含本数按照一个小时填报</div> <div style="font-size: 12px; color: #f00d0dff; margin-top: -12px; margin-bottom: 12px;">:不足30分钟不含本数不计入时长超过30分钟不足1个小时的含本数按照一个小时填报</div>
</a-col> </a-col>
</a-row> </a-row>
</template>
<!-- 活动信息 --> <template v-else-if="currentActivityType === 'TIME'">
<!-- time类型显示案件编号和服务时长在一行 -->
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="8"> <a-col :span="8">
<a-form-item label="活动类型" name="activityCategoryId"> <a-form-item label="案件编号" name="recordNo">
<CategoryTree <a-input style="width: 100%" v-model:value="form.recordNo" placeholder="请输入案件编号" :disabled="readonlyMode" />
v-model:value="form.activityCategoryId"
:category-type="CATEGORY_TYPE_ENUM.GOODS.value"
placeholder="请选择活动类型"
style="width: 100%"
:disabled="readonlyMode"
@change="onActivityCategoryChange"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="16">
<a-form-item label="活动名称" name="activityNameId"> <!-- 时间类型活动显示服务时长如果需要用户填写 -->
<a-select <a-col :span="8" v-if="currentActivity.price && currentActivity.price.includes('-')">
v-model:value="form.activityNameId" <a-form-item name="serviceDuration">
placeholder="请选择活动名称" <template #label>
style="width: 100%" <span>服务时长小时</span>
:disabled="!form.activityCategoryId || readonlyMode" <span style="color: #999; font-size: 12px; margin-left: 4px;">请在{{ currentActivity.price }}范围内填写</span>
:options="activityList" </template>
show-search <a-input-number style="width: 100%" v-model:value="form.serviceDuration" placeholder="服务时长(小时)" :disabled="readonlyMode" />
:filter-option="filterActivityOption" </a-form-item>
/> </a-col>
<!-- 时间类型活动显示固定服务时长 -->
<a-col :span="8" v-else-if="currentActivity.price && !currentActivity.price.includes('-')">
<a-form-item label="服务时长(小时)">
<a-input-number style="width: 100%" v-model:value="form.serviceDuration" :disabled="true" />
</a-form-item>
</a-col>
<!-- 时间类型活动显示默认服务时长当活动没有price字段时 -->
<a-col :span="8" v-else>
<a-form-item label="服务时长(小时)">
<a-input-number style="width: 100%" v-model:value="form.serviceDuration" :disabled="true" />
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
</template>
<template v-else-if="currentActivityType === 'AMOUT'">
<!-- amount类型显示金额输入框 -->
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="金额" name="workloadScore">
<a-input-number style="width: 100%" v-model:value="form.workloadScore" placeholder="请输入金额" :disabled="readonlyMode" :min="0" :precision="2" />
</a-form-item>
</a-col>
</a-row>
</template>
<!-- 服务统计信息 --> <!-- 服务统计信息 -->
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="8"> <a-col :span="8">
@ -162,7 +212,7 @@
readonly readonly
/> />
</div> </div>
<div v-else>无证明材料 (defaultFileList长度: {{ defaultFileList.length }})</div> <div v-else>无证明材料</div>
</template> </template>
@ -243,10 +293,17 @@
// //
const activityList = ref([]); const activityList = ref([]);
//
const currentActivity = ref({});
const currentActivityType = ref('');
// //
async function onActivityCategoryChange(categoryId) { async function onActivityCategoryChange(categoryId) {
// //
form.activityNameId = undefined; //if (!readonlyMode.value) {
//form.activityNameId = undefined;
//}
if (!categoryId) { if (!categoryId) {
activityList.value = []; activityList.value = [];
@ -265,11 +322,13 @@
const result = await goodsApi.queryGoodsList(queryParams); const result = await goodsApi.queryGoodsList(queryParams);
if (result.data && result.data.list) { if (result.data && result.data.list) {
// //
activityList.value = result.data.list.map(item => ({ activityList.value = result.data.list.map(item => ({
label: item.goodsName, label: item.goodsName,
value: item.goodsId, // ID value: item.goodsId, // ID
goodsId: item.goodsId, goodsId: item.goodsId,
timeType: item.timeType,
...item
})); }));
} else { } else {
activityList.value = []; activityList.value = [];
@ -327,6 +386,22 @@
form.attachmentIds = ''; form.attachmentIds = '';
} }
// ID
if (form.activityCategoryId) {
await onActivityCategoryChange(form.activityCategoryId);
// IDcurrentActivitycurrentActivityType
if (form.activityNameId && activityList.value.length > 0) {
const selectedActivity = activityList.value.find(activity => activity.goodsId === form.activityNameId);
if (selectedActivity) {
currentActivity.value = selectedActivity;
currentActivityType.value = selectedActivity.timeType || '';
// timeTypeserviceType
form.serviceType = selectedActivity.timeType || '';
}
}
}
// 使 // 使
// if (form.status && form.status.length > 0) { // if (form.status && form.status.length > 0) {
// form.status = form.status.map((e) => e.valueCode); // form.status = form.status.map((e) => e.valueCode);
@ -450,6 +525,9 @@
serviceDuration: undefined, // serviceDuration: undefined, //
activityCategoryId: undefined, // activityCategoryId: undefined, //
activityNameId: undefined, // activityNameId: undefined, //
recordNo: undefined, //
workloadScore: undefined, //
serviceType: undefined, //使timeType
beneficiaryCount: undefined, // beneficiaryCount: undefined, //
organizerName: undefined, // organizerName: undefined, //
organizerContact: undefined, // organizerContact: undefined, //
@ -469,7 +547,8 @@
watch( watch(
() => [form.serviceStart, form.serviceEnd], () => [form.serviceStart, form.serviceEnd],
([startTime, endTime]) => { ([startTime, endTime]) => {
if (startTime && endTime) { // DICT
if ((!form.applicationId || currentActivityType.value === 'DICT') && (startTime && endTime)) {
const start = dayjs(startTime); const start = dayjs(startTime);
const end = dayjs(endTime); const end = dayjs(endTime);
if (end.isAfter(start)) { if (end.isAfter(start)) {
@ -480,20 +559,141 @@
form.serviceDuration = undefined; form.serviceDuration = undefined;
message.warning('服务结束时间必须晚于开始时间'); message.warning('服务结束时间必须晚于开始时间');
} }
} else if (!form.applicationId && (!startTime || !endTime)) {
//
form.serviceDuration = undefined;
}
},
{ immediate: true }
);
//
watch(
() => form.activityNameId,
(activityNameId) => {
if (activityNameId) {
const selectedActivity = activityList.value.find(activity => activity.goodsId === activityNameId);
if (selectedActivity) {
currentActivity.value = selectedActivity;
currentActivityType.value = selectedActivity.timeType || '';
// timeTypeserviceType
form.serviceType = selectedActivity.timeType || '';
//
//
if (!form.applicationId) {
if (selectedActivity.timeType === 'TIME') {
// 使price
if (selectedActivity.price) {
// -
if (selectedActivity.price.includes('-')) {
form.serviceDuration = undefined;
} else {
// 使
form.serviceDuration = parseFloat(selectedActivity.price);
}
} else { } else {
//
form.serviceDuration = 25;
}
} else if (selectedActivity.timeType === 'AMOUT') {
form.serviceDuration = undefined;
} else if (selectedActivity.timeType === 'DICT') {
// DICT
form.serviceDuration = undefined; form.serviceDuration = undefined;
} }
}
}
} else {
currentActivity.value = {};
currentActivityType.value = '';
}
}, },
{ immediate: true } { immediate: true }
); );
const rules = { const rules = {
actualName: [{ required: true, message: '姓名 必填' }], actualName: [{ required: true, message: '姓名 必填' }],
certificateNumber: [{ required: true, message: '执业证号 必填' }], certificateNumber: [{ required: true, message: '执业证号 必填' }],
firmId: [{ required: true, message: '执业机构 必填' }], firmId: [{ required: true, message: '执业机构 必填' }],
serviceStart: [{ required: true, message: '服务开始时间 必填' }], serviceStart: [
serviceEnd: [{ required: true, message: '服务结束时间 必填' }], {
serviceDuration: [{ required: true, message: '服务时长(小时) 必填' }], validator: async (rule, value) => {
// dict
if (currentActivityType.value === 'DICT' && !value) {
return Promise.reject('服务开始时间 必填');
}
return Promise.resolve();
}
}
],
serviceEnd: [
{
validator: async (rule, value) => {
// dict
if (currentActivityType.value === 'DICT' && !value) {
return Promise.reject('服务结束时间 必填');
}
return Promise.resolve();
}
}
],
serviceDuration: [
{
validator: async (rule, value) => {
// dict
if (currentActivityType.value === 'DICT' && !value) {
return Promise.reject('服务时长(小时) 必填');
}
// time
if (currentActivityType.value === 'TIME') {
if (!value) {
return Promise.reject('服务时长(小时) 必填');
}
//
if (currentActivity.value.price && currentActivity.value.price.includes('-')) {
const [min, max] = currentActivity.value.price.split('-').map(Number);
if (value < min || value > max) {
return Promise.reject(`你填写的此类公益活动时长超出该活动要求的${currentActivity.value.price}小时,请按规范要求填报具体时长`);
}
}
}
// amount
if (currentActivityType.value === 'AMOUT') {
if (!value) {
return Promise.reject('服务时长(小时) 必填');
}
if (value < 100 || value > 200) {
return Promise.reject('你填写的此类公益活动时长超出该活动要求的100-200小时,请按规范要求填报具体时长');
}
}
return Promise.resolve();
}
}
],
recordNo: [
{
validator: async (rule, value) => {
// time
if (currentActivityType.value === 'TIME' && !value) {
return Promise.reject('案件编号 必填');
}
return Promise.resolve();
}
}
],
workloadScore: [
{
validator: async (rule, value) => {
// amount
if (currentActivityType.value === 'AMOUT' && !value) {
return Promise.reject('金额 必填');
}
return Promise.resolve();
}
}
],
activityCategoryId: [{ required: true, message: '活动类型 必填' }], activityCategoryId: [{ required: true, message: '活动类型 必填' }],
activityNameId: [{ required: true, message: '活动名称 必填' }], activityNameId: [{ required: true, message: '活动名称 必填' }],
beneficiaryCount: [{ required: true, message: '参加人数(受益人数) 必填' }], beneficiaryCount: [{ required: true, message: '参加人数(受益人数) 必填' }],
@ -560,6 +760,19 @@
async function save() { async function save() {
SmartLoading.show(); SmartLoading.show();
try { try {
//
if (form.recordNo) {
try {
const checkResult = await serviceApplicationsApi.checkCaseNumber(form.recordNo, form.applicationId);
if (!checkResult.ok) {
message.error(`本案仅限一人申报,已由${checkResult.data.lawyerName}律师申报`);
return;
}
} catch (error) {
console.error('案号校验失败:', error);
}
}
// //
const submitData = { ...form }; const submitData = { ...form };
@ -589,6 +802,19 @@
async function submit() { async function submit() {
SmartLoading.show(); SmartLoading.show();
try { try {
//
if (form.recordNo) {
try {
const checkResult = await serviceApplicationsApi.checkCaseNumber(form.recordNo, form.applicationId);
if (!checkResult.ok) {
message.error(`本案仅限一人申报,已由${checkResult.data.lawyerName}律师申报`);
return;
}
} catch (error) {
console.error('案号校验失败:', error);
}
}
// //
const submitData = { ...form }; const submitData = { ...form };
@ -597,13 +823,8 @@
submitData.attachmentIds = ''; submitData.attachmentIds = '';
} }
if (form.applicationId) { // submit
// submitapplicationId await serviceApplicationsApi.submit(submitData);
await serviceApplicationsApi.submit(form.applicationId);
} else {
// addSubmit
await serviceApplicationsApi.addSubmit(submitData);
}
message.success('提交成功'); message.success('提交成功');
emits('reloadList'); emits('reloadList');
onClose(); onClose();

15
src/views/business/erp/service/service-applications-list.vue

@ -155,7 +155,7 @@
:pagination="false" :pagination="false"
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }" :row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
> >
<template #bodyCell="{ text, record, column }"> <template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'action'"> <template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate"> <div class="smart-table-operate">
<a-button @click="showDetail(record)" type="link">详情</a-button> <a-button @click="showDetail(record)" type="link">详情</a-button>
@ -350,6 +350,16 @@ import AgreementModal from '/@/views/system/home/components/agreement-modal.vue'
dataIndex: 'serviceDuration', dataIndex: 'serviceDuration',
ellipsis: true, ellipsis: true,
}, },
{
title: '金额',
dataIndex: 'workloadScore',
ellipsis: true,
},
{
title: '案号',
dataIndex: 'recordNo',
ellipsis: true,
},
{ {
title: '参加人数(受益人数)', title: '参加人数(受益人数)',
dataIndex: 'beneficiaryCount', dataIndex: 'beneficiaryCount',
@ -871,7 +881,8 @@ onMounted(async () => {
async function requestSubmit(data){ async function requestSubmit(data){
try { try {
SmartLoading.show(); SmartLoading.show();
await serviceApplicationsApi.submit(data.applicationId); //
await serviceApplicationsApi.submit(data);
message.success('提交成功'); message.success('提交成功');
queryData(); queryData();
} catch (e) { } catch (e) {

361
src/views/mobile/components/agreement-modal.vue

@ -0,0 +1,361 @@
<template>
<div v-if="visible" class="agreement-modal-overlay" @click="handleCancel">
<div class="agreement-modal" @click.stop>
<!-- 头部 -->
<div class="modal-header">
<h3 class="modal-title">{{ noticeDetail.title || '平台协议' }}</h3>
</div>
<!-- 内容 -->
<div class="modal-content">
<div class="agreement-content" @scroll="handleScroll">
<div v-if="noticeDetail.title" class="agreement-text">
<div class="content-header">
<h4>{{ noticeDetail.title }}</h4>
<div class="content-header-info">
<span v-if="noticeDetail.author">作者{{ noticeDetail.author }}</span>
<span v-if="noticeDetail.source">来源{{ noticeDetail.source }}</span>
<span v-if="noticeDetail.publishTime">发布时间{{ noticeDetail.publishTime }}</span>
</div>
</div>
<div class="content-html" v-html="noticeDetail.contentHtml"></div>
<div v-if="!noticeDetail.contentHtml" class="default-content">
<p>请仔细阅读本承诺书...</p>
</div>
<p class="important">请仔细阅读以上协议内容点击"同意并继续"表示您已阅读并同意本协议</p>
</div>
<div v-else class="agreement-text">
<h4>用户协议与承诺书</h4>
<div class="default-content">
<p>请仔细阅读本承诺书...</p>
</div>
<p class="important">请仔细阅读以上协议内容点击"同意并继续"表示您已阅读并同意本协议</p>
</div>
</div>
</div>
<!-- 底部按钮 -->
<div class="modal-footer">
<button class="btn btn-secondary" @click="handleCancel">不同意</button>
<button
class="btn btn-primary"
@click="handleConfirm"
:disabled="!hasScrolledToBottom"
>
同意并继续
<span v-if="!hasScrolledToBottom">(请阅读完全文后点击)</span>
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, reactive, watch } from 'vue'
import { useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
import { letterApi } from '/@/api/business/letter/letter-api'
import { noticeApi } from '/@/api/business/oa/notice-api'
const router = useRouter()
const emit = defineEmits(['confirm', 'cancel'])
//
const props = defineProps({
visible: {
type: Boolean,
default: false
}
})
//
const loading = ref(false)
const hasScrolledToBottom = ref(false)
//
const noticeDetail = reactive({
title: '',
contentHtml: '',
author: '',
source: '',
publishTime: ''
})
// visible
watch(() => props.visible, (newVisible) => {
if (newVisible) {
getNoticeDetail()
}
}, { immediate: true })
onMounted(() => {
if (props.visible) {
getNoticeDetail()
}
})
//
async function getNoticeDetail() {
try {
loading.value = true
//
const result = await noticeApi.getOne({})
console.log('获取承诺书详情结果:', result)
if (result.code === 0 && result.data) {
Object.assign(noticeDetail, result.data)
} else {
//
Object.assign(noticeDetail, {
title: '用户协议与承诺书',
contentHtml: '',
author: '系统管理员',
source: '平台管理',
publishTime: new Date().toLocaleDateString()
})
}
} catch (error) {
console.error('获取承诺书详情失败:', error)
//
Object.assign(noticeDetail, {
title: '用户协议与承诺书',
contentHtml: '',
author: '系统管理员',
source: '平台管理',
publishTime: new Date().toLocaleDateString()
})
} finally {
loading.value = false
}
}
//
function handleScroll(event) {
const element = event.target
//
if (element.scrollTop + element.clientHeight >= element.scrollHeight - 10) {
hasScrolledToBottom.value = true
}
}
//
async function handleConfirm() {
try {
//
const signRes = await letterApi.add()
if (signRes.code === 0) {
message.success('承诺书签署成功')
emit('confirm')
} else {
message.error(signRes.msg || '承诺书签署失败')
}
} catch (error) {
console.error('签署承诺书失败:', error)
message.error('承诺书签署失败,请稍后重试')
}
}
//
function handleCancel() {
emit('cancel')
//
router.push('/login')
}
</script>
<style scoped>
/* 遮罩层 */
.agreement-modal-overlay {
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;
}
/* 弹框容器 */
.agreement-modal {
width: 90%;
max-width: 500px;
background-color: white;
border-radius: 12px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
max-height: 80vh;
}
/* 头部 */
.modal-header {
padding: 16px;
border-bottom: 1px solid #e8e8e8;
}
.modal-title {
font-size: 18px;
font-weight: 600;
margin: 0;
text-align: center;
color: #333;
}
/* 内容区域 */
.modal-content {
flex: 1;
overflow: hidden;
padding: 0 16px;
}
.agreement-content {
height: 50vh;
overflow-y: auto;
padding: 16px 0;
}
/* 滚动条样式 */
.agreement-content::-webkit-scrollbar {
width: 6px;
}
.agreement-content::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.agreement-content::-webkit-scrollbar-thumb {
background: #888;
border-radius: 3px;
}
.agreement-content::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* 内容样式 */
.agreement-text {
line-height: 1.6;
color: #555;
}
.content-header {
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid #f0f0f0;
}
.content-header h4 {
margin: 0 0 8px 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.content-header-info {
font-size: 12px;
color: #999;
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.content-html {
margin-bottom: 16px;
}
/* 默认内容 */
.default-content {
margin-bottom: 16px;
padding: 16px;
background-color: #fafafa;
border-radius: 6px;
font-size: 14px;
line-height: 1.6;
}
/* 重要提示 */
.important {
font-size: 14px;
color: #ff4d4f;
font-weight: 500;
text-align: center;
margin-top: 24px;
}
/* 底部按钮 */
.modal-footer {
padding: 16px;
border-top: 1px solid #e8e8e8;
display: flex;
gap: 12px;
justify-content: space-between;
}
.btn {
flex: 1;
padding: 12px;
font-size: 16px;
font-weight: 500;
border: none;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s;
}
.btn-primary {
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
color: white;
}
.btn-primary:hover {
opacity: 0.9;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
}
.btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.btn-secondary {
background-color: #f0f0f0;
color: #666;
border: 1px solid #d9d9d9;
}
.btn-secondary:hover {
background-color: #e8e8e8;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* 响应式设计 */
@media (max-width: 375px) {
.agreement-modal {
width: 95%;
max-width: none;
}
.modal-title {
font-size: 16px;
}
.modal-footer {
flex-direction: column;
}
.btn {
font-size: 14px;
padding: 10px;
}
}
</style>

6
src/views/mobile/index.vue

@ -80,7 +80,8 @@ async function getUserInfo() {
// PC // PC
function checkUserRole() { function checkUserRole() {
if (userInfo.value) { if (userInfo.value) {
const userRole = userInfo.value.roleCode || userInfo.value.roleName || '' // 使roleCode
const userRole = userInfo.value.roleCode || ''
const roleLower = userRole.toLowerCase() const roleLower = userRole.toLowerCase()
// CEOCEO // CEOCEO
@ -90,7 +91,8 @@ function checkUserRole() {
isAssociationRole.value = roleLower.includes('协会') || isAssociationRole.value = roleLower.includes('协会') ||
roleLower.includes('association') || roleLower.includes('association') ||
roleLower.includes('律协') || roleLower.includes('律协') ||
roleLower.includes('律师协会') roleLower.includes('律师协会') ||
roleLower.includes('admin_employee')
console.log('用户角色:', userRole, '是CEO:', isCeo.value, '是协会角色:', isAssociationRole.value) console.log('用户角色:', userRole, '是CEO:', isCeo.value, '是协会角色:', isAssociationRole.value)
} }

6
src/views/mobile/login.vue

@ -37,10 +37,6 @@
</div> </div>
</div> </div>
<div class="form-group">
<a-checkbox v-model:checked="rememberPwd">记住密码</a-checkbox>
</div>
<button <button
type="button" type="button"
@click="onLogin" @click="onLogin"
@ -235,7 +231,7 @@ onMounted(() => {
margin-bottom: 20px; margin-bottom: 20px;
} }
.form-group label { .form-group bel {
display: block; display: block;
margin-bottom: 8px; margin-bottom: 8px;
font-weight: 500; font-weight: 500;

1042
src/views/mobile/service/create.vue

File diff suppressed because it is too large

597
src/views/mobile/service/detail.vue

@ -88,50 +88,12 @@
<div class="form-section"> <div class="form-section">
<div class="section-title">服务信息</div> <div class="section-title">服务信息</div>
<!-- 服务开始时间 -->
<div class="form-item">
<label class="form-label">服务开始时间</label>
<input
v-model="form.serviceStart"
class="form-input"
placeholder="服务开始时间"
:readonly="readonlyMode"
/>
</div>
<!-- 服务结束时间 -->
<div class="form-item">
<label class="form-label">服务结束时间</label>
<input
v-model="form.serviceEnd"
class="form-input"
placeholder="服务结束时间"
:readonly="readonlyMode"
/>
</div>
<!-- 服务时长 -->
<div class="form-item">
<label class="form-label">服务时长小时</label>
<input
v-model="form.serviceDuration"
class="form-input"
placeholder="服务时长(小时)"
:readonly="readonlyMode"
/>
</div>
</div>
<!-- 活动信息 -->
<div class="form-section">
<div class="section-title">活动信息</div>
<!-- 活动类型 --> <!-- 活动类型 -->
<div class="form-item"> <div class="form-item">
<label class="form-label">活动类型</label> <label class="form-label">活动类型</label>
<template v-if="readonlyMode"> <template v-if="readonlyMode">
<input <input
v-model="form.activityCategoryName" v-model="form.activityCategory"
class="form-input" class="form-input"
placeholder="活动类型" placeholder="活动类型"
readonly readonly
@ -152,6 +114,10 @@
{{ category.categoryName }} {{ category.categoryName }}
</option> </option>
</select> </select>
<!-- 编辑模式下显示当前活动类型名称 -->
<div v-if="form.activityCategoryId && form.activityCategory" class="form-tip">
当前活动类型{{ form.activityCategory }}
</div>
</template> </template>
</div> </div>
@ -159,6 +125,7 @@
<div class="form-item"> <div class="form-item">
<label class="form-label">活动名称</label> <label class="form-label">活动名称</label>
<template v-if="readonlyMode"> <template v-if="readonlyMode">
<!-- 只读模式直接显示后端返回的活动名称 -->
<input <input
v-model="form.activityName" v-model="form.activityName"
class="form-input" class="form-input"
@ -167,6 +134,7 @@
/> />
</template> </template>
<template v-else> <template v-else>
<!-- 编辑模式使用下拉选择框 -->
<select <select
v-model="form.activityNameId" v-model="form.activityNameId"
class="form-select" class="form-select"
@ -184,6 +152,122 @@
</template> </template>
</div> </div>
<!-- dict类型显示服务时长服务开始时间和服务结束时间 -->
<template v-if="activityType === 'DICT'">
<!-- 服务开始时间 -->
<div class="form-item">
<label class="form-label">服务开始时间</label>
<a-date-picker
show-time
format="YYYY-MM-DD HH:00:00"
valueFormat="YYYY-MM-DD HH:00:00"
v-model:value="form.serviceStart"
class="form-input"
placeholder="服务开始时间"
:disabled="readonlyMode"
/>
</div>
<!-- 服务结束时间 -->
<div class="form-item">
<label class="form-label">服务结束时间</label>
<a-date-picker
show-time
format="YYYY-MM-DD HH:00:00"
valueFormat="YYYY-MM-DD HH:00:00"
v-model:value="form.serviceEnd"
class="form-input"
placeholder="服务结束时间"
:disabled="readonlyMode"
/>
</div>
<!-- 服务时长 -->
<div class="form-item">
<label class="form-label">
服务时长小时
<span style="color: #999; font-size: 12px; margin-left: 4px;">可通过时间选择也可手动填写</span>
</label>
<input
v-model="form.serviceDuration"
class="form-input"
placeholder="服务时长(小时)"
:readonly="readonlyMode"
/>
<div style="font-size: 12px; color: #f00d0d; margin-top: 6px;">:不足30分钟不含本数不计入时长超过30分钟不足1个小时的含本数按照一个小时填报</div>
</div>
</template>
<!-- time类型显示案件编号输入框没有服务开始时间和服务结束时间 -->
<template v-else-if="activityType === 'TIME'">
<!-- 案件编号 -->
<div class="form-item">
<label class="form-label">案件编号</label>
<input
v-model="form.recordNo"
class="form-input"
placeholder="请输入案件编号"
:readonly="readonlyMode"
/>
</div>
<!-- 服务时长 -->
<div class="form-item">
<label class="form-label">
服务时长小时
<span v-if="selectedActivity.price && selectedActivity.price.includes('-')" style="color: #999; font-size: 12px; margin-left: 4px;">请在{{ selectedActivity.price }}范围内填写</span>
</label>
<input
v-model="form.serviceDuration"
class="form-input"
placeholder="服务时长(小时)"
:readonly="readonlyMode || (selectedActivity.price && !selectedActivity.price.includes('-'))"
/>
</div>
</template>
<!-- amount类型显示金额输入框 -->
<template v-else-if="activityType === 'AMOUT'">
<!-- 金额 -->
<div class="form-item">
<label class="form-label">金额</label>
<input
type="number"
step="0.01"
v-model="form.workloadScore"
class="form-input"
placeholder="请输入金额"
:readonly="readonlyMode"
/>
</div>
<!-- 服务时长 -->
<div class="form-item">
<label class="form-label">服务时长小时</label>
<input
v-model="form.serviceDuration"
class="form-input"
placeholder="服务时长(小时)"
:readonly="readonlyMode"
/>
<div style="font-size: 12px; color: #f00d0d; margin-top: 6px;">:服务时长必须在100-200小时范围内</div>
</div>
</template>
<!-- 加载中状态 -->
<template v-else>
<div class="form-item">
<div style="text-align: center; padding: 20px; color: #999;">
加载中...
</div>
</div>
</template>
</div>
<!-- 活动信息 -->
<div class="form-section">
<div class="section-title">活动信息</div>
<!-- 参加人数受益人数 --> <!-- 参加人数受益人数 -->
<div class="form-item"> <div class="form-item">
<label class="form-label">参加人数受益人数</label> <label class="form-label">参加人数受益人数</label>
@ -253,7 +337,10 @@
<!-- 文件列表 --> <!-- 文件列表 -->
<div class="form-item"> <div class="form-item">
<label class="form-label">证明材料</label> <label class="form-label">
证明材料
<span style="font-size: 12px; color: #f00d0d; margin-left: 4px;"> :请上传活动方案活动记录照片新闻报道等材料支持图片(JPG/PNG)文档(PDF/Word/PPT)格式单文件最大10MB最多上传5个文件</span>
</label>
<div class="file-list" v-if="uploadedFiles.length > 0"> <div class="file-list" v-if="uploadedFiles.length > 0">
<div <div
v-for="file in uploadedFiles" v-for="file in uploadedFiles"
@ -312,25 +399,40 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 承诺书弹框 -->
<AgreementModal
v-if="showAgreementModal"
:visible="showAgreementModal"
@confirm="handleAgreementConfirm"
@cancel="handleAgreementCancel"
/>
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted, watch, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { serviceApplicationsApi } from '/@/api/business/service-applications/service-applications-api' import { serviceApplicationsApi } from '/@/api/business/service-applications/service-applications-api'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import dayjs from 'dayjs'
import { loginApi } from '/@/api/system/login-api' import { loginApi } from '/@/api/system/login-api'
import { positionApi } from '/@/api/system/position-api' import { positionApi } from '/@/api/system/position-api'
import { categoryApi } from '/@/api/business/category/category-api' import { categoryApi } from '/@/api/business/category/category-api'
import { goodsApi } from '/@/api/business/goods/goods-api' import { goodsApi } from '/@/api/business/goods/goods-api'
import { fileApi } from '/@/api/support/file-api' import { fileApi } from '/@/api/support/file-api'
import { letterApi } from '/@/api/business/letter/letter-api'
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const' import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const'
import AgreementModal from '../components/agreement-modal.vue'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const loading = ref(false) const loading = ref(false)
const readonlyMode = ref(true) // const readonlyMode = ref(true) //
//
const showAgreementModal = ref(false)
const agreementSigned = ref(true)
// //
const form = reactive({ const form = reactive({
applicationId: undefined, //ID applicationId: undefined, //ID
@ -347,6 +449,11 @@ const form = reactive({
activityCategoryId: '', //ID activityCategoryId: '', //ID
activityNameId: '', //ID activityNameId: '', //ID
activityName: '', // activityName: '', //
activityCategory: '', //
activityCategoryName: '', //
recordNo: '', //
workloadScore: null, //
serviceType: '', //使timeType
beneficiaryCount: null, // beneficiaryCount: null, //
organizerName: '', // organizerName: '', //
organizerContact: '', // organizerContact: '', //
@ -359,12 +466,139 @@ const form = reactive({
auditRemark: '' // auditRemark: '' //
}) })
//
const currentActivity = ref({});
const currentActivityType = ref('');
//
const activityType = computed(() => {
return currentActivityType.value || '';
});
//
const selectedActivity = computed(() => {
return currentActivity.value || {};
});
//
watch(
() => [form.serviceStart, form.serviceEnd],
([startTime, endTime]) => {
if (startTime && endTime) {
const start = dayjs(startTime);
const end = dayjs(endTime);
if (end.isAfter(start)) {
// 2
const durationMinutes = end.diff(start, 'minute');
form.serviceDuration = parseFloat((durationMinutes / 60).toFixed(2));
} else {
form.serviceDuration = undefined;
message.warning('服务结束时间必须晚于开始时间');
}
} else {
form.serviceDuration = undefined;
}
},
{ immediate: true }
)
//
watch(
() => form.activityNameId,
(activityNameId) => {
if (activityNameId) {
const selectedActivity = activityList.value.find(activity => activity.goodsId === activityNameId);
if (selectedActivity) {
currentActivity.value = selectedActivity;
currentActivityType.value = selectedActivity.timeType || '';
// timeTypeserviceType
form.serviceType = selectedActivity.timeType || '';
//
if (selectedActivity.timeType === 'TIME') {
// 使price
if (selectedActivity.price) {
// -
if (selectedActivity.price.includes('-')) {
form.serviceDuration = undefined;
} else {
// 使
form.serviceDuration = parseFloat(selectedActivity.price);
}
} else {
//
form.serviceDuration = 25;
}
} else if (selectedActivity.timeType === 'AMOUT') {
form.serviceDuration = undefined;
} else if (selectedActivity.timeType === 'DICT') {
// DICT
form.serviceDuration = undefined;
}
}
} else {
currentActivity.value = {};
currentActivityType.value = '';
form.serviceType = '';
}
},
{ immediate: true }
)
// //
const positionList = ref([]) const positionList = ref([])
const activityCategoryList = ref([]) const activityCategoryList = ref([])
const activityList = ref([]) const activityList = ref([])
const uploadedFiles = ref([]) const uploadedFiles = ref([])
//
async function getUserInfo() {
try {
const res = await loginApi.getLoginInfo()
const userInfo = res.data
//
console.log('用户签约状态 agreementSignFlag:', userInfo.agreementSignFlag)
agreementSigned.value = userInfo.agreementSignFlag === true
showAgreementModal.value = !agreementSigned.value
} catch (error) {
console.error('获取用户信息失败:', error)
}
}
// -
async function handleAgreementConfirm() {
try {
await letterApi.add()
message.success('承诺书签署成功')
showAgreementModal.value = false
agreementSigned.value = true
} catch (error) {
message.error('承诺书签署失败,请重新登录')
console.error('承诺书签署失败:', error)
// 退
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
//
setTimeout(() => {
router.push('/mobile/login')
}, 1500)
}
}
//
function handleAgreementCancel() {
message.warning('您需要同意平台协议才能使用系统')
showAgreementModal.value = false
// 退
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
//
setTimeout(() => {
router.push('/mobile/login')
}, 1500)
}
// //
async function getDetail() { async function getDetail() {
const applicationId = route.query.applicationId const applicationId = route.query.applicationId
@ -376,26 +610,86 @@ async function getDetail() {
loading.value = true loading.value = true
try { try {
//
await getUserInfo()
const res = await serviceApplicationsApi.queryDetail(applicationId) const res = await serviceApplicationsApi.queryDetail(applicationId)
if (res.code === 0) { if (res.code === 0) {
console.log('后端返回数据:', res.data)
console.log('后端返回的活动名称ID:', res.data.activityNameId)
console.log('后端返回的活动名称:', res.data.activityName)
console.log('后端返回的活动类型ID:', res.data.activityCategoryId)
console.log('后端返回的活动类型:', res.data.activityCategory)
Object.assign(form, res.data) Object.assign(form, res.data)
console.log('赋值后表单数据:', form)
console.log('赋值后的活动名称ID:', form.activityNameId)
console.log('赋值后的活动名称:', form.activityName)
console.log('赋值后的活动类型ID:', form.activityCategoryId)
console.log('赋值后的活动类型:', form.activityCategory)
// //
// status=0稿 // status=0稿
// status>=1 // status>=1
readonlyMode.value = form.status !== 0 readonlyMode.value = form.status !== 0
// 稿 console.log('当前模式:', readonlyMode.value ? '只读模式' : '编辑模式')
if (!readonlyMode.value) {
//
console.log('开始获取下拉选项数据...')
//
await getSelectOptions() await getSelectOptions()
console.log('下拉选项数据获取完成')
console.log('活动类型列表:', activityCategoryList.value)
// ID
if (form.activityCategoryId) {
console.log('有活动类型ID,开始获取活动列表:', form.activityCategoryId)
await onActivityCategoryChange()
} else {
console.log('没有活动类型ID,跳过获取活动列表')
} }
// 5. IDPC //
console.log('页面加载完成 - 活动类型ID:', form.activityCategoryId)
console.log('页面加载完成 - 活动名称ID:', form.activityNameId)
console.log('页面加载完成 - 活动列表:', activityList.value)
// IDcurrentActivitycurrentActivityType
if (form.activityNameId && activityList.value.length > 0) {
// 使
let selectedActivity = activityList.value.find(activity => activity.goodsId === form.activityNameId)
if (!selectedActivity) {
//
selectedActivity = activityList.value.find(activity => activity.goodsId == form.activityNameId)
}
if (selectedActivity) {
currentActivity.value = selectedActivity
currentActivityType.value = selectedActivity.timeType || ''
// timeTypeserviceType
form.serviceType = selectedActivity.timeType || ''
console.log('更新后的活动类型:', currentActivityType.value)
console.log('更新后的活动信息:', currentActivity.value)
} else {
console.log('未找到对应的活动:', form.activityNameId)
console.log('活动列表:', activityList.value)
}
} else {
console.log('活动名称ID或活动列表为空:', form.activityNameId, activityList.value.length)
}
// IDPC
if (form.attachmentIds && form.attachmentIds.trim()) { if (form.attachmentIds && form.attachmentIds.trim()) {
await getFileListByAttachmentIds(form.attachmentIds) await getFileListByAttachmentIds(form.attachmentIds)
} }
console.log('详情数据加载完成:', form) console.log('详情数据加载完成:', form)
console.log('活动列表:', activityList.value)
console.log('活动名称ID:', form.activityNameId)
console.log('活动名称:', form.activityName)
console.log('附件ID:', form.attachmentIds) console.log('附件ID:', form.attachmentIds)
console.log('文件列表:', uploadedFiles.value) console.log('文件列表:', uploadedFiles.value)
} else { } else {
@ -460,6 +754,14 @@ async function getSelectOptions() {
categoryType: 1 // categoryType: 1 //
}) })
activityCategoryList.value = categoryRes.data || [] activityCategoryList.value = categoryRes.data || []
console.log('获取活动类型列表成功:', activityCategoryList.value.length, '条记录')
// ID
if (form.activityCategoryId) {
console.log('有活动类型ID,准备获取活动列表:', form.activityCategoryId)
await onActivityCategoryChange()
}
} catch (error) { } catch (error) {
console.error('获取选项数据失败:', error) console.error('获取选项数据失败:', error)
} }
@ -467,23 +769,64 @@ async function getSelectOptions() {
// //
async function onActivityCategoryChange() { async function onActivityCategoryChange() {
console.log('活动类型改变事件被调用 - 活动类型ID:', form.activityCategoryId)
if (form.activityCategoryId) { if (form.activityCategoryId) {
try { try {
//
activityList.value = []
const activityRes = await goodsApi.queryGoodsList({ const activityRes = await goodsApi.queryGoodsList({
categoryId: form.activityCategoryId, categoryId: form.activityCategoryId,
shelvesFlag: true, shelvesFlag: true,
pageNum: 1, pageNum: 1,
pageSize: 100 pageSize: 100
}) })
activityList.value = activityRes.data?.list || []
console.log('获取活动列表成功:', activityRes.data?.list?.length || 0, '条记录')
if (activityRes.data && activityRes.data.list) {
// PC
activityList.value = activityRes.data.list.map(item => ({
label: item.goodsName,
value: item.goodsId,
goodsId: item.goodsId,
goodsName: item.goodsName,
timeType: item.timeType,
price: item.price,
...item
}))
console.log('活动列表转换完成,包含timeType:', activityList.value.map(item => ({ goodsId: item.goodsId, timeType: item.timeType, price: item.price })))
console.log('活动列表转换完成:', activityList.value)
// ID
if (form.activityNameId) {
const activityExists = activityList.value.some(item => item.goodsId === form.activityNameId)
console.log('检查活动是否存在:', form.activityNameId, '=>', activityExists)
if (!activityExists) {
//
form.activityNameId = ''
console.log('清空活动名称选择')
}
}
} else {
activityList.value = []
form.activityNameId = ''
console.log('活动列表为空,清空选择')
}
} catch (error) { } catch (error) {
console.error('获取活动列表失败:', error) console.error('获取活动列表失败:', error)
activityList.value = [] activityList.value = []
form.activityNameId = ''
} }
} else { } else {
activityList.value = [] activityList.value = []
}
form.activityNameId = '' form.activityNameId = ''
console.log('没有活动类型ID,清空活动列表和选择')
}
} }
// //
@ -495,6 +838,83 @@ function handleBack() {
async function handleSave() { async function handleSave() {
if (readonlyMode.value) return if (readonlyMode.value) return
//
if (!form.positionId) {
message.error('请选择职务')
return
}
if (!form.activityCategoryId) {
message.error('请选择活动类型')
return
}
if (!form.activityNameId) {
message.error('请选择活动名称')
return
}
//
if (currentActivityType.value === 'DICT') {
// DICT
if (!form.serviceStart) {
message.error('请选择服务开始时间')
return
}
if (!form.serviceEnd) {
message.error('请选择服务结束时间')
return
}
if (!form.serviceDuration) {
message.error('请填写服务时长')
return
}
} else if (currentActivityType.value === 'TIME') {
// TIME
if (!form.recordNo) {
message.error('请填写案件编号')
return
}
if (!form.serviceDuration) {
message.error('请填写服务时长')
return
}
//
if (currentActivity.value.price && currentActivity.value.price.includes('-')) {
const [min, max] = currentActivity.value.price.split('-').map(Number)
if (form.serviceDuration < min || form.serviceDuration > max) {
message.error(`你填写的此类公益活动时长超出该活动要求的${currentActivity.value.price}小时,请按规范要求填报具体时长`)
return
}
}
} else if (currentActivityType.value === 'AMOUT') {
// AMOUNT
if (!form.workloadScore) {
message.error('请填写金额')
return
}
if (!form.serviceDuration) {
message.error('请填写服务时长')
return
}
// 100-200
if (form.serviceDuration < 100 || form.serviceDuration > 200) {
message.error('你填写的此类公益活动时长超出该活动要求的100-200小时,请按规范要求填报具体时长')
return
}
}
if (!form.serviceContent) {
message.error('请输入服务内容描述')
return
}
loading.value = true loading.value = true
try { try {
const submitData = { const submitData = {
@ -528,6 +948,19 @@ async function handleSubmit() {
return return
} }
if (!form.activityCategoryId) {
message.error('请选择活动类型')
return
}
if (!form.activityNameId) {
message.error('请选择活动名称')
return
}
//
if (currentActivityType.value === 'DICT') {
// DICT
if (!form.serviceStart) { if (!form.serviceStart) {
message.error('请选择服务开始时间') message.error('请选择服务开始时间')
return return
@ -538,15 +971,48 @@ async function handleSubmit() {
return return
} }
if (!form.activityCategoryId) { if (!form.serviceDuration) {
message.error('请选择活动类型') message.error('请填写服务时长')
return
}
} else if (currentActivityType.value === 'TIME') {
// TIME
if (!form.recordNo) {
message.error('请填写案件编号')
return return
} }
if (!form.activityNameId) { if (!form.serviceDuration) {
message.error('请选择活动名称') message.error('请填写服务时长')
return
}
//
if (currentActivity.value.price && currentActivity.value.price.includes('-')) {
const [min, max] = currentActivity.value.price.split('-').map(Number)
if (form.serviceDuration < min || form.serviceDuration > max) {
message.error(`你填写的此类公益活动时长超出该活动要求的${currentActivity.value.price}小时,请按规范要求填报具体时长`)
return
}
}
} else if (currentActivityType.value === 'AMOUT') {
// AMOUNT
if (!form.workloadScore) {
message.error('请填写金额')
return
}
if (!form.serviceDuration) {
message.error('请填写服务时长')
return
}
// 100-200
if (form.serviceDuration < 100 || form.serviceDuration > 200) {
message.error('你填写的此类公益活动时长超出该活动要求的100-200小时,请按规范要求填报具体时长')
return return
} }
}
if (!form.serviceContent) { if (!form.serviceContent) {
message.error('请输入服务内容描述') message.error('请输入服务内容描述')
@ -603,6 +1069,15 @@ function getStatusClass(status) {
return classMap[status] || 'status-unknown' return classMap[status] || 'status-unknown'
} }
//
function getActivityCategoryName() {
if (!form.activityCategoryId) return ''
//
const category = activityCategoryList.value.find(item => item.categoryId === form.activityCategoryId)
return category ? category.categoryName : ''
}
// HTML // HTML
function stripHtmlTags(html) { function stripHtmlTags(html) {
if (!html) return '' if (!html) return ''
@ -610,6 +1085,28 @@ function stripHtmlTags(html) {
return html.replace(/<[^>]*>/g, '').trim() return html.replace(/<[^>]*>/g, '').trim()
} }
//
async function getActivityCategoryList() {
try {
//
const categoryRes = await categoryApi.queryCategoryTree({
categoryType: 1 //
})
activityCategoryList.value = categoryRes.data || []
} catch (error) {
console.error('获取活动类型列表失败:', error)
}
}
//
function getActivityName() {
if (!form.activityNameId) return ''
//
const activity = activityList.value.find(item => item.goodsId === form.activityNameId)
return activity ? activity.ativityName : ''
}
onMounted(() => { onMounted(() => {
getDetail() getDetail()
}) })

12
vite.config.js

@ -35,20 +35,20 @@ export default {
proxy: { proxy: {
// 代理API路径 // 代理API路径
'/api': { '/api': {
//target: 'http://8.148.67.92:8080/', // 目标服务器地址 target: 'http://8.148.67.92:8080/', // 目标服务器地址
target: 'http://127.0.0.1:8080/', //target: 'http://127.0.0.1:8080/',
changeOrigin: true, // 是否修改请求头中的 Origin 字段 changeOrigin: true, // 是否修改请求头中的 Origin 字段
rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径 rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径
}, },
'/login': { '/login': {
//target: 'http://8.148.67.92:8080/', // 目标服务器地址 target: 'http://8.148.67.92:8080/', // 目标服务器地址
target: 'http://127.0.0.1:8080/', //target: 'http://127.0.0.1:8080/',
changeOrigin: true, // 是否修改请求头中的 Origin 字段 changeOrigin: true, // 是否修改请求头中的 Origin 字段
rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径 rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径
}, },
'/mobile/login': { '/mobile/login': {
//target: 'http://8.148.67.92:8080/', // 目标服务器地址 target: 'http://8.148.67.92:8080/', // 目标服务器地址
target: 'http://127.0.0.1:8080/', //target: 'http://127.0.0.1:8080/',
changeOrigin: true, // 是否修改请求头中的 Origin 字段 changeOrigin: true, // 是否修改请求头中的 Origin 字段
rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径 rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径
}, },

Loading…
Cancel
Save