generated from Leo_Ding/web-template
Merge branch 'master' of https://gitlab.guxuan.icu/Leo_Ding/hahaPension_admin
This commit is contained in:
commit
9feb188430
3
.env.dev
3
.env.dev
@ -12,7 +12,8 @@ VITE_ROUTER_BASE=/
|
||||
VITE_ROUTER_HISTORY=hash
|
||||
|
||||
# api
|
||||
VITE_API_BASIC='http://10.10.1.39:8040'
|
||||
# VITE_API_BASIC='http://10.10.1.39:8040'
|
||||
VITE_API_BASIC='http://115.239.217.220:8021'
|
||||
VITE_API_UPLOAD='http://115.239.217.220:9458'
|
||||
VITE_API_HTTP=/api/v1/
|
||||
# storage
|
||||
|
||||
6825
package-lock.json
generated
6825
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -51,7 +51,7 @@
|
||||
"xy-storage": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.3.1",
|
||||
"@vitejs/plugin-vue": "^5.0.0",
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
@ -60,7 +60,7 @@
|
||||
"less": "^4.2.0",
|
||||
"lint-staged": "^14.0.0",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"vite": "^4.4.9",
|
||||
"vite": "^5.0.0",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-progress": "^0.0.7",
|
||||
|
||||
@ -41,6 +41,7 @@ const theme = ref({
|
||||
background-repeat: no-repeat;
|
||||
background-color: transparent;
|
||||
padding: 20px;
|
||||
color: #ffffff;
|
||||
}
|
||||
.bottom-center .amap-info-sharp:after {
|
||||
|
||||
|
||||
@ -13,4 +13,12 @@ export const updateItem = (params) => request.basic.put(`/api/v1/service-people/
|
||||
// 删除数据
|
||||
export const delItem = (id) => request.basic.delete(`/api/v1/service-people/${id}`)
|
||||
|
||||
//获取服务项目树状数据
|
||||
export const getTreeData=(params)=>request.basic.get('/api/v1/service-projects/category_project',params)
|
||||
|
||||
//绑定服务项目
|
||||
export const createProject=(params)=>request.basic.post('/api/v1/service-person-projects/many',params)
|
||||
//获取服务人员绑定的服务项目
|
||||
|
||||
export const getProject=(params)=>request.basic.get('/api/v1/service-person-projects',params)
|
||||
|
||||
|
||||
@ -1,18 +1,23 @@
|
||||
<!-- AreaCascader.vue -->
|
||||
<template>
|
||||
<a-cascader v-model:value="modelValue" :options="options" :load-data="loadData" :placeholder="placeholder"
|
||||
<a-cascader v-model:value="innerValue" :options="options" :load-data="loadData" :placeholder="placeholder"
|
||||
:style="style" :disabled="disabled" :show-search="showSearch" :allow-clear="allowClear"
|
||||
:change-on-select="changeOnSelect" :field-names="fieldNames" @change="handleChange" ref="cascaderRef" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, nextTick, defineModel, onMounted,defineExpose } from 'vue';
|
||||
import { ref, watch, nextTick, onMounted, defineProps, defineEmits, defineExpose } from 'vue';
|
||||
import apis from '@/apis';
|
||||
import { useDicsStore } from '@/store';
|
||||
|
||||
const dicsStore = useDicsStore();
|
||||
const modelValue = defineModel();
|
||||
|
||||
// 🔸 使用 defineProps 接收 v-model 和其他 props
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: { type: String, default: '请选择省市区' },
|
||||
style: { type: Object, default: () => ({ width: '100%' }) },
|
||||
disabled: { type: Boolean, default: false },
|
||||
@ -23,26 +28,50 @@ const props = defineProps({
|
||||
type: Object,
|
||||
default: () => ({
|
||||
label: 'label',
|
||||
value: 'code',
|
||||
// children: 'children' // 注意:如果后端字段是 children,可以放开
|
||||
value: 'code'
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['change']);
|
||||
const options = ref([]);
|
||||
// 🔸 显式定义 emit,支持 v-model 和 change 事件
|
||||
const emit = defineEmits(['update:modelValue', 'change']);
|
||||
|
||||
// 🔁 监听 store 的省份数据
|
||||
// 🔸 内部响应式值,用于绑定 a-cascader 的 v-model:value
|
||||
const innerValue = ref([]);
|
||||
|
||||
// 🔁 监听外部 modelValue 变化(用于回显)
|
||||
watch(
|
||||
() => dicsStore.provinceOptions,
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal && newVal.length > 0) {
|
||||
options.value = newVal;
|
||||
if (newVal && typeof newVal === 'string') {
|
||||
const codes = newVal.split(',').filter(Boolean);
|
||||
innerValue.value = codes;
|
||||
// 如果 options 已加载,尝试初始化完整路径
|
||||
if (options.value.length > 0 && codes.length > 1) {
|
||||
initData(codes);
|
||||
}
|
||||
} else {
|
||||
innerValue.value = [];
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 🔁 监听 store 的省份数据
|
||||
const options = ref([]);
|
||||
watch(
|
||||
() => dicsStore.provinceOptions,
|
||||
(newVal) => {
|
||||
if (newVal && newVal.length > 0) {
|
||||
options.value = newVal;
|
||||
// 如果已有值,尝试初始化
|
||||
if (innerValue.value.length > 1) {
|
||||
initData([...innerValue.value]);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 🔍 递归查找节点
|
||||
function findNodeInOptions(options, code) {
|
||||
@ -55,37 +84,14 @@ function findNodeInOptions(options, code) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function initData(codes){
|
||||
// 从第二级开始,逐级模拟加载
|
||||
for (let i = 1; i < codes.length; i++) {
|
||||
const parentCode = codes[i - 1];
|
||||
// const currentCode = val[i];
|
||||
// 查找当前选中的父节点
|
||||
let targetNode;
|
||||
if (i === 1) {
|
||||
// 第一级(省)在 options 中
|
||||
targetNode = options.value.find(opt => opt.code === parentCode);
|
||||
} else {
|
||||
// 更深层级,需要递归查找
|
||||
targetNode = findNodeInOptions(options.value, parentCode);
|
||||
}
|
||||
|
||||
if (targetNode && !targetNode.children) {
|
||||
// 触发 loadData 加载子级
|
||||
await loadData([targetNode]);
|
||||
}
|
||||
|
||||
// 等待加载完成
|
||||
await nextTick();
|
||||
}
|
||||
}
|
||||
// 🌐 异步加载子节点
|
||||
const loadData = async (selectedOptions) => {
|
||||
const targetOption = selectedOptions[selectedOptions.length - 1];
|
||||
targetOption.loading = true;
|
||||
|
||||
try {
|
||||
const parentId = targetOption.id || targetOption.code; // 兼容 id 或 code
|
||||
const parentId = targetOption.id || targetOption.code;
|
||||
const response = await apis.common.getAreaList({
|
||||
current: 1,
|
||||
pageSize: 100,
|
||||
@ -98,16 +104,15 @@ const loadData = async (selectedOptions) => {
|
||||
const children = response.data.map(item => ({
|
||||
...item,
|
||||
isLeaf: !item.hasChild,
|
||||
label: item.label || item.name, // 兼容字段
|
||||
label: item.label || item.name,
|
||||
value: item.code
|
||||
}));
|
||||
|
||||
targetOption.children = children;
|
||||
} else {
|
||||
targetOption.children = [];
|
||||
}
|
||||
|
||||
// 触发视图更新
|
||||
// 触发更新
|
||||
options.value = [...options.value];
|
||||
} catch (error) {
|
||||
console.error('加载子节点失败:', error);
|
||||
@ -118,8 +123,38 @@ const loadData = async (selectedOptions) => {
|
||||
|
||||
// 📣 处理选择变化
|
||||
const handleChange = (value, selectedOptions) => {
|
||||
console.log(value)
|
||||
// value 是 code 数组,如 ['110000', '110100', '110101']
|
||||
const fullCode = value ? value.join(',') : '';
|
||||
innerValue.value = value || [];
|
||||
|
||||
// 更新父组件的 v-model
|
||||
emit('update:modelValue', fullCode);
|
||||
// 触发 change 事件(传递 code 数组和 label 数组)
|
||||
emit('change', value, selectedOptions?.map(opt => opt.label) || []);
|
||||
};
|
||||
defineExpose({initData})
|
||||
|
||||
// 🧩 初始化已选路径(供父组件调用)
|
||||
async function initData(codes) {
|
||||
if (!codes || codes.length <= 1) return;
|
||||
|
||||
for (let i = 1; i < codes.length; i++) {
|
||||
const parentCode = codes[i - 1];
|
||||
let targetNode;
|
||||
|
||||
if (i === 1) {
|
||||
targetNode = options.value.find(opt => opt.code === parentCode);
|
||||
} else {
|
||||
targetNode = findNodeInOptions(options.value, parentCode);
|
||||
}
|
||||
|
||||
if (targetNode && !targetNode.children) {
|
||||
await loadData([targetNode]);
|
||||
}
|
||||
|
||||
await nextTick();
|
||||
}
|
||||
}
|
||||
|
||||
// 💡 暴露方法给父组件(如手动初始化)
|
||||
defineExpose({ initData });
|
||||
</script>
|
||||
@ -126,7 +126,7 @@ const handleMapClick = (e) => {
|
||||
content: `<div style="padding:10px;min-width:200px;">
|
||||
<div style="font-weight:bold;margin-bottom:5px;">点击位置</div>
|
||||
<div>${address}</div>
|
||||
<div style="margin-top:8px;color:#666;">
|
||||
<div style="margin-top:8px;color:#fff;">
|
||||
<div>经度: ${lng.toFixed(6)}</div>
|
||||
<div>纬度: ${lat.toFixed(6)}</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<!-- @/components/TreeSelect.vue -->
|
||||
<template>
|
||||
<a-tree-select v-model:value="modelValue" show-search :style="{ width: width }"
|
||||
<a-tree-select v-model:value="innerValue" show-search :style="{ width: width }"
|
||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }" :placeholder="placeholder" :allow-clear="allowClear"
|
||||
:tree-default-expand-all="defaultExpandAll" :tree-data="localTreeData" :tree-node-filter-prop="filterProp"
|
||||
:disabled="disabled" :loading="loading" @change="handleChange" @search="handleSearch" @focus="handleFocus"
|
||||
@ -10,11 +10,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted,defineModel } from 'vue'
|
||||
import { useDicsStore } from '@/store'
|
||||
const dicsStore = useDicsStore()
|
||||
const modelValue = defineModel();
|
||||
import { ref, watch, onMounted, defineProps, defineEmits } from 'vue';
|
||||
import { useDicsStore } from '@/store';
|
||||
|
||||
const dicsStore = useDicsStore();
|
||||
|
||||
// 🔸 定义 props(包含 v-model 的 modelValue)
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number, Array], // 根据实际值类型调整,a-tree-select 可能是 string/array
|
||||
default: ''
|
||||
},
|
||||
allowClear: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@ -39,62 +45,75 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否启用从 store 自动加载(可选)
|
||||
useStore: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'change', 'search', 'focus', 'blur'])
|
||||
// 🔸 定义 emits
|
||||
const emit = defineEmits(['update:modelValue', 'change', 'search', 'focus', 'blur']);
|
||||
|
||||
// 🔸 内部响应式值,用于绑定 a-tree-select 的 v-model:value
|
||||
const innerValue = ref(props.modelValue);
|
||||
|
||||
const localTreeData = ref([])
|
||||
const loading = ref(false)
|
||||
// 🔁 监听外部 modelValue 变化(支持父组件动态更新值)
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
innerValue.value = newVal;
|
||||
}
|
||||
);
|
||||
|
||||
// 🔁 监听 store 数据变化
|
||||
const localTreeData = ref([]);
|
||||
const loading = ref(false);
|
||||
|
||||
// 监听 store 数据变化(响应式)
|
||||
watch(
|
||||
() => dicsStore.orgTree,
|
||||
(newVal) => {
|
||||
if (newVal?.length > 0) {
|
||||
localTreeData.value = newVal
|
||||
console.log('localTreeData',localTreeData.value)
|
||||
// modelValue.value=localTreeData.value[0].value
|
||||
loading.value = false
|
||||
if (Array.isArray(newVal) && newVal.length > 0) {
|
||||
localTreeData.value = newVal;
|
||||
loading.value = false;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
);
|
||||
|
||||
// 组件挂载时:如果启用了 store,且数据未加载,则主动加载
|
||||
// 📥 组件挂载时自动加载(如果启用)
|
||||
onMounted(async () => {
|
||||
if (props.useStore) {
|
||||
if (dicsStore.orgTree.length === 0 && !loading.value) {
|
||||
loading.value = true
|
||||
loading.value = true;
|
||||
try {
|
||||
await dicsStore.loadOrgTree()
|
||||
// 数据会通过 watch 自动同步
|
||||
await dicsStore.loadOrgTree();
|
||||
// 数据会通过 watch 自动同步到 localTreeData
|
||||
} catch (error) {
|
||||
loading.value = false
|
||||
console.error('加载组织树失败:', error);
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// 📤 处理选择变更
|
||||
const handleChange = (value, label, extra) => {
|
||||
emit('change', value, label, extra)
|
||||
}
|
||||
innerValue.value = value;
|
||||
emit('update:modelValue', value); // 触发 v-model 更新
|
||||
emit('change', value, label, extra);
|
||||
};
|
||||
|
||||
// 🔍 搜索
|
||||
const handleSearch = (value) => {
|
||||
console.log(value)
|
||||
emit('search', value)
|
||||
}
|
||||
emit('search', value);
|
||||
};
|
||||
|
||||
// 💡 聚焦/失焦
|
||||
const handleFocus = () => {
|
||||
emit('focus')
|
||||
}
|
||||
emit('focus');
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
emit('blur')
|
||||
}
|
||||
emit('blur');
|
||||
};
|
||||
</script>
|
||||
@ -7,23 +7,30 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref,defineModel } from 'vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import apis from '@/apis'
|
||||
|
||||
// 使用 defineModel 简化双向绑定
|
||||
// 它会自动生成 { currentOrg: String, onUpdateCurrentOrg: Function }
|
||||
// 并返回一个可读写的 ref
|
||||
const model = defineModel('currentOrg', { type: String, default: '' })
|
||||
const model = ref(props.currentOrg)
|
||||
|
||||
// 接收其他不需要双向绑定的 prop
|
||||
const props = defineProps({
|
||||
defaultOpen: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
currentOrg: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['change']) // 保留 change 事件
|
||||
// 监听父组件 prop 变化(可选,如果父组件会动态改 currentOrg)
|
||||
watch(() => props.currentOrg, (newVal) => {
|
||||
model.value = newVal
|
||||
})
|
||||
const emit = defineEmits(['update:currentOrg', 'change']) // 保留 change 事件
|
||||
|
||||
// 获取数据
|
||||
const stationList = ref([])
|
||||
@ -44,7 +51,8 @@ async function getData() {
|
||||
|
||||
// 处理 change 事件
|
||||
function handleChange(e) {
|
||||
// model.value 会自动同步(不需要手动赋值)
|
||||
model.value = e
|
||||
emit('update:currentOrg', e) // 触发 v-model 更新
|
||||
emit('change', e)
|
||||
}
|
||||
</script>
|
||||
@ -1,12 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="brand"
|
||||
:class="cpClass"
|
||||
:style="cpStyle">
|
||||
<img
|
||||
alt=""
|
||||
:src="config('app.logo')" />
|
||||
<h1>{{ config('app.title') }}</h1>
|
||||
<div class="brand" :class="cpClass" :style="cpStyle">
|
||||
<!-- <img alt="" src="/public/logo-hahayun.png" /> -->
|
||||
<h1 style="color:#7babdb">{{ Title }}</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -15,7 +10,7 @@ import { storeToRefs } from 'pinia'
|
||||
import { computed } from 'vue'
|
||||
import { config } from '@/config'
|
||||
import { useAppStore } from '@/store'
|
||||
|
||||
import storage from '@/utils/storage'
|
||||
defineOptions({
|
||||
name: 'Brand',
|
||||
})
|
||||
@ -49,6 +44,10 @@ const cpStyle = computed(() => {
|
||||
height: `${appConfig.value.headerHeight}px`,
|
||||
}
|
||||
})
|
||||
const Title = computed(() => {
|
||||
const platForm = storage.local.getItem('platform')
|
||||
return platForm == 'jianguan' ? '智慧养老居家监管平台' : platForm == 'yunying' ? '智慧养老居家运营平台' : '智慧养老居家呼叫平台'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@ -46,17 +46,17 @@ export default [
|
||||
permission: '*',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'institution',
|
||||
name: 'institution',
|
||||
component: 'serverObj/institution/index.vue',
|
||||
meta: {
|
||||
title: '机构服务对象列表',
|
||||
isMenu: true,
|
||||
keepAlive: true,
|
||||
permission: '*',
|
||||
},
|
||||
},
|
||||
// {
|
||||
// path: 'institution',
|
||||
// name: 'institution',
|
||||
// component: 'serverObj/institution/index.vue',
|
||||
// meta: {
|
||||
// title: '机构服务对象列表',
|
||||
// isMenu: true,
|
||||
// keepAlive: true,
|
||||
// permission: '*',
|
||||
// },
|
||||
// },
|
||||
{
|
||||
path: 'toBeInstitution',
|
||||
name: 'toBeInstitution',
|
||||
|
||||
@ -86,8 +86,7 @@
|
||||
<span v-else>女</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag v-if="record.status == '1'" color="green">启用</a-tag>
|
||||
<a-tag v-else color="red">禁用</a-tag>
|
||||
<a-tag>{{ dicsStore.getDictLabel('STAFF_STATUS', record.status) }}</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'serviceType'">
|
||||
<span>{{ dicsStore.getDictLabel('STAFF_TYPE', record.serviceType) }}</span>
|
||||
@ -100,11 +99,13 @@
|
||||
<x-action-button @click="$refs.detailRef.handleEdit(record)">
|
||||
<span>详情</span>
|
||||
</x-action-button>
|
||||
<x-action-button @click="$refs.editDialogRef.handleEdit(record)" v-if="platForm==='yunying'">
|
||||
<x-action-button @click="treeHandleEdit(record.id)" v-if="platForm === 'yunying'">
|
||||
<span>服务项目</span>
|
||||
</x-action-button>
|
||||
<x-action-button @click="$refs.editDialogRef.handleEdit(record)" v-if="platForm==='yunying'">
|
||||
<span>停用</span>
|
||||
<x-action-button @click="handleStop(record)"
|
||||
v-if="platForm === 'yunying' && (record.status == '1' || record.status == '4')">
|
||||
<span v-if="record.status === '1'">停用</span>
|
||||
<span v-if="record.status === '4'">启用</span>
|
||||
</x-action-button>
|
||||
<x-action-button @click="handleDelete(record)">
|
||||
<span style="color: #ff4d4f;">删除</span>
|
||||
@ -117,6 +118,19 @@
|
||||
</a-row>
|
||||
<edit-dialog ref="editDialogRef" @ok="onOk"></edit-dialog>
|
||||
<detail ref="detailRef"></detail>
|
||||
<a-modal v-model:open="treeOpen" title="绑定服务项目" @ok="handleOk">
|
||||
<a-card>
|
||||
<a-spin :spinning="spinning">
|
||||
<a-tree v-model:expandedKeys="expandedKeys" v-model:selectedKeys="selectedKeys"
|
||||
v-model:checkedKeys="checkedKeys" checkable :tree-data="treeData" @check="onCheck">
|
||||
<template #title="{ title, key }">
|
||||
<span v-if="key === '0-0-1-0'" style="color: #1890ff">{{ title }}</span>
|
||||
<template v-else>{{ title }}</template>
|
||||
</template>
|
||||
</a-tree>
|
||||
</a-spin>
|
||||
</a-card>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -133,9 +147,17 @@ import detail from './components/detail.vue'
|
||||
import dayjs from 'dayjs'
|
||||
import NodeTree from '@/components/NodeTree/index.vue'
|
||||
import storage from '@/utils/storage'
|
||||
import { getBirthDate, spliceUrl } from '@/utils/util'
|
||||
defineOptions({
|
||||
name: 'allocation',
|
||||
})
|
||||
const currentId = ref('')
|
||||
const spinning = ref(false)
|
||||
const expandedKeys = ref([])
|
||||
const selectedKeys = ref([])
|
||||
const checkedKeys = ref([])
|
||||
const treeData = ref([])
|
||||
const treeOpen = ref(false)
|
||||
const dicsStore = useDicsStore()
|
||||
const platForm = storage.local.getItem('platform')
|
||||
const columns = [
|
||||
@ -228,6 +250,7 @@ getPageList()
|
||||
|
||||
async function getPageList() {
|
||||
try {
|
||||
loading.value = true
|
||||
const { pageSize, current } = paginationState
|
||||
const { success, data, total } = await apis.serviceStaffList
|
||||
.getProjectList({
|
||||
@ -244,32 +267,126 @@ async function getPageList() {
|
||||
if (config('http.code.success') === success) {
|
||||
listData.value = data
|
||||
paginationState.total = total
|
||||
loading.value = false
|
||||
getServerProject()
|
||||
}
|
||||
} catch (error) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
// 判断是否为叶子节点
|
||||
function isLeafNode(nodeKey, data = treeData.value) {
|
||||
for (const item of data) {
|
||||
if (item.key === nodeKey) {
|
||||
return !item.children || item.children.length === 0;
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
const found = isLeafNode(nodeKey, item.children);
|
||||
if (found !== null) return found;
|
||||
}
|
||||
}
|
||||
return null; // 未找到
|
||||
}
|
||||
|
||||
// 获取所有叶子节点(递归)
|
||||
function getAllLeafKeys(data) {
|
||||
let leaves = [];
|
||||
data.forEach(item => {
|
||||
if (!item.children || item.children.length === 0) {
|
||||
leaves.push(item.key);
|
||||
} else {
|
||||
leaves = leaves.concat(getAllLeafKeys(item.children));
|
||||
}
|
||||
});
|
||||
return leaves;
|
||||
}
|
||||
|
||||
// 处理勾选
|
||||
const onCheck = (checkedKeysObj, info) => {
|
||||
console.log(checkedKeysObj)
|
||||
// const { checked, halfChecked } = checkedKeysObj;
|
||||
|
||||
// 只保留叶子节点
|
||||
const leafCheckedKeys = checkedKeysObj.filter(key => {
|
||||
return isLeafNode(key) === true;
|
||||
});
|
||||
|
||||
// 更新受控的 checkedKeys(可选:只显示叶子被选中)
|
||||
checkedKeys.value = leafCheckedKeys;
|
||||
};
|
||||
/**核销 */
|
||||
const getServerProject = async (id) => {
|
||||
try {
|
||||
currentId.value = id
|
||||
const { data, success } = await apis.serviceStaffList.getTreeData({ categoryType: '1' })
|
||||
if (success) {
|
||||
treeData.value = transformCategories(data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error.msg)
|
||||
}
|
||||
}
|
||||
async function treeHandleEdit(id){
|
||||
try {
|
||||
currentId.value=id
|
||||
spinning.value=true
|
||||
treeOpen.value = true
|
||||
const {data,success}=await apis.serviceStaffList.getProject({current:1,pageSize:99,servicePersonId:id})
|
||||
if (success) {
|
||||
checkedKeys.value=data.map(item=>item.projectId)
|
||||
console.log(selectedKeys.value)
|
||||
}
|
||||
spinning.value=false
|
||||
} catch (error) {
|
||||
spinning.value=false
|
||||
}
|
||||
|
||||
}
|
||||
function transformCategories(data) {
|
||||
return data.map(category => ({
|
||||
key: category.categoryId,
|
||||
title: category.categoryName,
|
||||
children: category.projects.map(project => ({
|
||||
key: project.id,
|
||||
title: project.name
|
||||
}))
|
||||
}));
|
||||
}
|
||||
/**核销 */
|
||||
const checkHandler = (record) => {
|
||||
Modal.confirm({
|
||||
title: '即将核销是否继续',
|
||||
content: t('button.confirm'),
|
||||
okText: t('button.confirm'),
|
||||
onOk: async () => {
|
||||
const params = {
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
const params = { servicePersonId: currentId.value, projectIds: checkedKeys.value }
|
||||
const {success} = await apis.serviceStaffList.createProject(params)
|
||||
if(success){
|
||||
message.success('绑定成功')
|
||||
treeOpen.value=false
|
||||
checkedKeys.value=[]
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
}
|
||||
const handleStop = async (record) => {
|
||||
let params = {
|
||||
...record,
|
||||
status: 'success'
|
||||
birthDay: record.birthDate2
|
||||
}
|
||||
const { success } = await apis.productOrder.updateItem(params.id, params).catch(() => {
|
||||
// throw new Error()
|
||||
})
|
||||
if (config('http.code.success') === success) {
|
||||
// resolve()
|
||||
message.success('核销成功')
|
||||
await getPageList()
|
||||
|
||||
if (record.laborContract && record.laborContract.length > 0) {
|
||||
params.laborContractStartAt = record.laborContract[0]
|
||||
params.laborContractEndAt = record.laborContract[1]
|
||||
}
|
||||
},
|
||||
params.imgs = (record.imgs && record.imgs.length) > 0 ? record.imgs.map(item => spliceUrl(item)) : []
|
||||
params.attachments = (record.attachments && record.attachments.length) > 0 ? record.attachments.map(item => spliceUrl(item)) : []
|
||||
params.commissionRate = record.commissionRate ?? record.commissionRate / 100
|
||||
params.status = record.status == '4' ? '1' : '4'
|
||||
console.log(params)
|
||||
const { success } = await apis.serviceStaffList.updateItem(params).catch(() => {
|
||||
throw new Error()
|
||||
})
|
||||
if (success) {
|
||||
getPageList()
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 删除
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="24">
|
||||
<a-form-item label="类型" name="categoryType" >
|
||||
<a-select v-model:value="formData.categoryType" allowClear>
|
||||
<a-select v-model:value="formData.categoryType" allowClear :disabled="categoryDisabled">
|
||||
<a-select-option v-for="item in dicsStore.dictOptions.PROJECT_TYPE" :key="item.dval"
|
||||
:value="item.dval">{{
|
||||
item.introduction }}</a-select-option>
|
||||
@ -138,6 +138,7 @@ const { modal, showModal, hideModal, showLoading, hideLoading } = useModal()
|
||||
const { formRecord, formData, formRef, formRules, resetForm } = useForm()
|
||||
const cancelText = ref('取消')
|
||||
const dicsStore = useDicsStore()
|
||||
const categoryDisabled=ref(false)
|
||||
formRules.value = {
|
||||
categoryType: [{ required: true, message: '请选择类型', trigger: ['blur', 'change'] }],
|
||||
categoryId: [{ required: true, message: '请选择类型', trigger: ['blur', 'change'] }],
|
||||
@ -158,6 +159,7 @@ function handleCreate() {
|
||||
type: 'create',
|
||||
title: '新增',
|
||||
})
|
||||
categoryDisabled.value=false
|
||||
formData.value = {
|
||||
priceUnit: '元/次',
|
||||
frequencyUnit: '元/次',
|
||||
@ -170,8 +172,9 @@ function handleCreate() {
|
||||
function handleEdit(record = {}) {
|
||||
showModal({
|
||||
type: 'edit',
|
||||
title: '编辑',
|
||||
title: '编辑11',
|
||||
})
|
||||
categoryDisabled.value=true
|
||||
formRecord.value = record
|
||||
formData.value = cloneDeep(record)
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<a-col :span="6">
|
||||
<a-form-item label="项目分类" name="categoryId">
|
||||
<a-select v-model:value="searchFormData.categoryId" allowClear>
|
||||
<a-select-option v-for="item in categoryList" :key="item.id" :value="item.name">{{
|
||||
<a-select-option v-for="item in categoryList" :key="item.id" :value="item.id">{{
|
||||
item.name }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
@ -50,7 +50,9 @@
|
||||
<template v-if="column.key === 'categoryType'">
|
||||
<span>{{ dicsStore.getDictLabel('PROJECT_TYPE', record.categoryType) }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="column.key === 'categoryId'">
|
||||
<span>{{categoryList.find(item=>item.id==record.categoryId).name}}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'price'">
|
||||
<span>{{ record.price + record.frequencyUnit }}</span>
|
||||
</template>
|
||||
@ -86,7 +88,7 @@ const categoryList=ref([])
|
||||
const columns = [
|
||||
{ title: '序号', dataIndex: 'serialNumber', key: 'serialNumber', align: 'center', width: 80, },
|
||||
{ title: '类型', dataIndex: 'categoryType', key: 'categoryType', align: 'center', width: 100, },
|
||||
{ title: '分类名称', dataIndex: 'name', key: 'name', align: 'center', width: 180, },
|
||||
{ title: '分类名称', dataIndex: 'categoryId', key: 'categoryId', align: 'center', width: 180, },
|
||||
{ title: '项目名称', dataIndex: 'name', key: 'name', align: 'center', },
|
||||
{ title: '价格', dataIndex: 'price', key: 'price', align: 'center', },
|
||||
{ title: '简介', dataIndex: 'content', key: 'content', align: 'center', },
|
||||
|
||||
@ -564,51 +564,54 @@ async function handleEdit(record = {}) {
|
||||
function isValidIdCard(value) {
|
||||
if (!value || typeof value !== 'string') return false;
|
||||
|
||||
const idCardRegex = /(^\d{15}$)|(^\d{18}$)|(^\d{18}X$)/i;
|
||||
if (!idCardRegex.test(value)) return false;
|
||||
// 清洗:去空格、转大写
|
||||
const id = value.trim().toUpperCase();
|
||||
|
||||
// 18位校验码验证
|
||||
if (value.length === 18) {
|
||||
const Wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1];
|
||||
const Vi = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
|
||||
// 精确正则:15位纯数字,或18位(前17位数字 + 最后一位数字或X)
|
||||
const reg = /^(\d{15}|\d{17}[\dX])$/;
|
||||
if (!reg.test(id)) return false;
|
||||
|
||||
let sum = 0;
|
||||
for (let i = 0; i < 17; i++) {
|
||||
sum += parseInt(value[i], 10) * Wi[i];
|
||||
}
|
||||
const checkCode = Vi[sum % 11];
|
||||
if (checkCode !== value[17].toUpperCase()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const len = id.length;
|
||||
|
||||
// 验证生日
|
||||
let year, month, day;
|
||||
if (value.length === 15) {
|
||||
year = '19' + value.substring(6, 8);
|
||||
month = value.substring(8, 10);
|
||||
day = value.substring(10, 12);
|
||||
if (len === 15) {
|
||||
year = '19' + id.substring(6, 8);
|
||||
month = id.substring(8, 10);
|
||||
day = id.substring(10, 12);
|
||||
} else {
|
||||
year = value.substring(6, 10);
|
||||
month = value.substring(10, 12);
|
||||
day = value.substring(12, 14);
|
||||
year = id.substring(6, 10);
|
||||
month = id.substring(10, 12);
|
||||
day = id.substring(12, 14);
|
||||
}
|
||||
|
||||
const date = new Date(year, month - 1, day);
|
||||
if (
|
||||
date.getFullYear() !== parseInt(year, 10) ||
|
||||
date.getMonth() + 1 !== parseInt(month, 10) ||
|
||||
date.getDate() !== parseInt(day, 10)
|
||||
) {
|
||||
const y = parseInt(year, 10);
|
||||
const m = parseInt(month, 10);
|
||||
const d = parseInt(day, 10);
|
||||
const now = new Date();
|
||||
if (y < 1900 || y > now.getFullYear() || m < 1 || m > 12 || d < 1 || d > 31) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
const birthYear = parseInt(year, 10);
|
||||
if (birthYear < 1900 || birthYear > currentYear) {
|
||||
const date = new Date(y, m - 1, d);
|
||||
if (date.getFullYear() !== y || date.getMonth() + 1 !== m || date.getDate() !== d) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 18位校验码
|
||||
if (len === 18) {
|
||||
const Wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
|
||||
const Vi = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
|
||||
let sum = 0;
|
||||
for (let i = 0; i < 17; i++) {
|
||||
sum += parseInt(id[i], 10) * Wi[i];
|
||||
}
|
||||
const checkCode = Vi[sum % 11];
|
||||
if (checkCode !== id[17]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
@ -633,6 +636,7 @@ function handleOk() {
|
||||
// params.stationId=storage.local.getItem('stationId')
|
||||
// 单独封装一个同步校验函数
|
||||
if (params.identityType === '1' && !isValidIdCard(params.identityNo)) {
|
||||
hideLoading()
|
||||
return message.error('请输入正确的身份证号码')
|
||||
}
|
||||
let result = null
|
||||
|
||||
@ -106,6 +106,9 @@
|
||||
<template v-if="column.key === 'categoryType'">
|
||||
<span>{{ dicsStore.getDictLabel('PROJECT_TYPE', record.categoryType) }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'categoryId'">
|
||||
<span>{{categoryList.find(item=>item.id==record.categoryId).name}}</span>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-modal>
|
||||
@ -117,7 +120,7 @@
|
||||
|
||||
<script setup>
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed,nextTick } from 'vue'
|
||||
import { config } from '@/config'
|
||||
import apis from '@/apis'
|
||||
import { useForm, useModal, usePagination } from '@/hooks'
|
||||
@ -142,6 +145,7 @@ const mapVisible = ref(false)
|
||||
const stationList = ref([])
|
||||
const spining = ref(false)
|
||||
const areaCascaderRef=ref()
|
||||
const categoryList = ref([])
|
||||
formRules.value = {
|
||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||
identityType: [{ required: true, message: '请选择证件类型', trigger: 'change' }],
|
||||
@ -169,7 +173,7 @@ const serviceList = ref([])
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{ title: '类型', dataIndex: 'categoryType', key: 'categoryType', align: 'center', width: 100, },
|
||||
{ title: '分类名称', dataIndex: 'name', key: 'name', align: 'center', width: 180, },
|
||||
{ title: '分类名称', dataIndex: 'categoryId', key: 'categoryId', align: 'center', width: 180, },
|
||||
{ title: '项目名称', dataIndex: 'name', key: 'name', align: 'center', },
|
||||
{ title: '价格', dataIndex: 'price', key: 'price', align: 'center', },
|
||||
{ title: '简介', dataIndex: 'remark', key: 'remark', align: 'center', },
|
||||
@ -190,6 +194,7 @@ const rowSelection = computed(() => ({
|
||||
}),
|
||||
}))
|
||||
getSeveicePeople()
|
||||
getCategoriesAll()
|
||||
async function getSeveicePeople() {
|
||||
try {
|
||||
const { success, data, total } = await apis.serviceStaffList
|
||||
@ -208,6 +213,21 @@ async function getSeveicePeople() {
|
||||
|
||||
}
|
||||
}
|
||||
async function getCategoriesAll() {
|
||||
try {
|
||||
const { success, data, total } = await apis.projectType
|
||||
.getProjectListAll({
|
||||
pageSize: 100, current: 1,
|
||||
})
|
||||
.catch(() => {
|
||||
throw new Error()
|
||||
})
|
||||
if (config('http.code.success') === success) {
|
||||
categoryList.value = data.map(item => ({ id: item.id, name: item.name }))
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
function onAreaChange(value, labels) {
|
||||
formData.value.areaLabels = [...labels]
|
||||
}
|
||||
@ -260,6 +280,7 @@ const handleRemove = (item) => {
|
||||
// 确认选择
|
||||
const handleConfirm = () => {
|
||||
modalVisible.value = false
|
||||
|
||||
}
|
||||
|
||||
// 取消
|
||||
@ -299,6 +320,7 @@ async function handleEdit(record = {}, type) {
|
||||
})
|
||||
if (type == '2') {
|
||||
formData.value.detailAddress = record.archive.homeDetailAddress
|
||||
console.log(record.archive.homeDetailAddress)
|
||||
gps.value = record.region.join('') + record.archive.homeDetailAddress
|
||||
formData.value.lat = record.archive.lag
|
||||
formData.value.lng = record.archive.lat
|
||||
@ -307,6 +329,7 @@ async function handleEdit(record = {}, type) {
|
||||
getServiceStation()
|
||||
spining.value = false
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
spining.value = false
|
||||
}
|
||||
|
||||
@ -352,7 +375,8 @@ function handleOk() {
|
||||
params.plannedServiceStartDate = formData.value.planeDate[0]
|
||||
params.plannedEndDate = formData.value.planeDate[1]
|
||||
}
|
||||
params.projects = serviceList.value
|
||||
params.projects =selectedServices.value
|
||||
console.log(params)
|
||||
let result = null
|
||||
switch (modal.value.type) {
|
||||
case 'create':
|
||||
@ -389,6 +413,7 @@ function handleOk() {
|
||||
*/
|
||||
function handleCancel() {
|
||||
selectedServices.value = []
|
||||
formData.value={}
|
||||
hideModal()
|
||||
}
|
||||
|
||||
|
||||
@ -244,7 +244,8 @@
|
||||
</template>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary" v-if="platForm!=='hujiao'" @click="$refs.editDialogRef.handleCreate()">新建</a-button>
|
||||
<a-button type="primary" v-if="platForm !== 'hujiao'"
|
||||
@click="$refs.editDialogRef.handleCreate()">新建</a-button>
|
||||
<a-dropdown v-if="platForm !== 'hujiao'">
|
||||
<template #overlay>
|
||||
<a-menu @click="handleMenuClick">
|
||||
@ -325,7 +326,7 @@
|
||||
<x-action-button @click="$refs.detailRef.handleEdit(record)">
|
||||
<span>详情</span>
|
||||
</x-action-button>
|
||||
<x-action-button v-if="platForm==='yunying'">
|
||||
<!-- <x-action-button v-if="platForm==='yunying'">
|
||||
<a-dropdown>
|
||||
<a class="ant-dropdown-link" @click.prevent>
|
||||
绑定
|
||||
@ -342,14 +343,14 @@
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</x-action-button>
|
||||
</x-action-button> -->
|
||||
<x-action-button @click="$refs.lineOrderRef.handleEdit(record, '2')">
|
||||
<span>线下工单</span>
|
||||
</x-action-button>
|
||||
<x-action-button @click="$refs.lineOrderRef.handleEdit(record, '1')"
|
||||
<!-- <x-action-button @click="$refs.lineOrderRef.handleEdit(record, '1')"
|
||||
v-if="platForm !== 'yunying'">
|
||||
<span>线上工单</span>
|
||||
</x-action-button>
|
||||
</x-action-button> -->
|
||||
<x-action-button @click="$refs.transferRef.handleCreate(record.id, record.stationId)">
|
||||
<span>转出</span>
|
||||
</x-action-button>
|
||||
@ -475,20 +476,23 @@ const columns = [
|
||||
// --- 时间字段:去世时间 ---
|
||||
{
|
||||
title: '去世时间',
|
||||
dataIndex: 'passWayAt',
|
||||
key: 'passWayAt',
|
||||
dataIndex: 'livingStatus.passWayAt',
|
||||
key: 'livingStatus.passWayAt',
|
||||
align: 'center',
|
||||
width: 140,
|
||||
customRender: ({ text, record }) => {
|
||||
return text ? dayjs(text).format('YYYY-MM-DD') : '-';
|
||||
return record.livingStatus.passWayAt ? dayjs(record.livingStatus.passWayAt).format('YYYY-MM-DD') : '-';
|
||||
},
|
||||
},
|
||||
// --- 去世原因 ---
|
||||
{
|
||||
title: '去世原因',
|
||||
dataIndex: 'passWayReason',
|
||||
key: 'passWayReason',
|
||||
dataIndex: 'livingStatus.passWayReason',
|
||||
key: 'livingStatus.passWayReason',
|
||||
align: 'center',
|
||||
customRender: ({ text, record }) => {
|
||||
return record.livingStatus.passWayReason;
|
||||
},
|
||||
width: 140,
|
||||
},
|
||||
// --- 更新时间 ---
|
||||
|
||||
@ -249,28 +249,7 @@ async function getPageList() {
|
||||
|
||||
}
|
||||
}
|
||||
/**核销 */
|
||||
const checkHandler = (record) => {
|
||||
Modal.confirm({
|
||||
title: '即将核销是否继续',
|
||||
content: t('button.confirm'),
|
||||
okText: t('button.confirm'),
|
||||
onOk: async () => {
|
||||
const params = {
|
||||
...record,
|
||||
status: 'success'
|
||||
}
|
||||
const { success } = await apis.productOrder.updateItem(params.id, params).catch(() => {
|
||||
// throw new Error()
|
||||
})
|
||||
if (config('http.code.success') === success) {
|
||||
// resolve()
|
||||
message.success('核销成功')
|
||||
await getPageList()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
@ -283,7 +262,7 @@ function handleDelete({ id }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
; (async () => {
|
||||
try {
|
||||
const { success } = await apis.productOrder.delItem(id).catch(() => {
|
||||
const { success } = await apis.serviceStaffList.delItem(id).catch(() => {
|
||||
throw new Error()
|
||||
})
|
||||
if (config('http.code.success') === success) {
|
||||
|
||||
@ -297,7 +297,7 @@
|
||||
<x-action-button @click="$refs.detailRef.handleCreate(record)">
|
||||
<span>详情</span>
|
||||
</x-action-button>
|
||||
<x-action-button v-if="platForm==='yunying'">
|
||||
<!-- <x-action-button v-if="platForm==='yunying'">
|
||||
<a-dropdown>
|
||||
<a class="ant-dropdown-link" @click.prevent>
|
||||
绑定
|
||||
@ -314,7 +314,7 @@
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</x-action-button>
|
||||
</x-action-button> -->
|
||||
<x-action-button @click="$refs.lineOrderRef.handleEdit(record, '2')">
|
||||
<span>线下工单</span>
|
||||
</x-action-button>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user