|
@@ -5,12 +5,15 @@
|
|
|
<div class="card-header">
|
|
<div class="card-header">
|
|
|
<span class="title">用户管理</span>
|
|
<span class="title">用户管理</span>
|
|
|
<div class="header-actions">
|
|
<div class="header-actions">
|
|
|
- <el-select v-model="searchForm.areaId" placeholder="所属区域" style="width: 150px; margin-right: 10px" clearable @change="onSearchAreaChange">
|
|
|
|
|
- <el-option v-for="area in areaList" :key="area.id" :label="area.name" :value="area.id" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <el-select v-model="searchForm.stationId" placeholder="所属站点" style="width: 150px; margin-right: 10px" clearable @change="handleSearch">
|
|
|
|
|
- <el-option v-for="station in filteredStationList" :key="station.id" :label="station.name" :value="station.id" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
|
|
+ <el-cascader
|
|
|
|
|
+ v-model="searchAreaValue"
|
|
|
|
|
+ :options="areaTreeOptions"
|
|
|
|
|
+ :props="{ checkStrictly: true, value: 'id', label: 'name' }"
|
|
|
|
|
+ placeholder="所属站点"
|
|
|
|
|
+ style="width: 350px; margin-right: 10px"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ @change="onSearchAreaChange"
|
|
|
|
|
+ />
|
|
|
<el-input v-model="searchForm.keyword" placeholder="搜索姓名/手机号" style="width: 200px; margin-right: 10px;" clearable @keyup.enter="handleSearch" @clear="handleSearch" />
|
|
<el-input v-model="searchForm.keyword" placeholder="搜索姓名/手机号" style="width: 200px; margin-right: 10px;" clearable @keyup.enter="handleSearch" @clear="handleSearch" />
|
|
|
<el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['archieves:customer:add']">新增用户</el-button>
|
|
<el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['archieves:customer:add']">新增用户</el-button>
|
|
|
</div>
|
|
</div>
|
|
@@ -108,6 +111,8 @@
|
|
|
v-model:visible="drawerVisible"
|
|
v-model:visible="drawerVisible"
|
|
|
:customer-id="currentUser.id"
|
|
:customer-id="currentUser.id"
|
|
|
editable
|
|
editable
|
|
|
|
|
+ :service-list="serviceList"
|
|
|
|
|
+ :area-station-list="allNodes"
|
|
|
@add-pet="openAddPet"
|
|
@add-pet="openAddPet"
|
|
|
@pet-detail="handlePetDetail"
|
|
@pet-detail="handlePetDetail"
|
|
|
@pet-edit="handlePetEdit"
|
|
@pet-edit="handlePetEdit"
|
|
@@ -119,7 +124,7 @@
|
|
|
<PetDetailDrawer
|
|
<PetDetailDrawer
|
|
|
v-model:visible="petDrawerVisible"
|
|
v-model:visible="petDrawerVisible"
|
|
|
:pet-id="currentPet.id"
|
|
:pet-id="currentPet.id"
|
|
|
- editable
|
|
|
|
|
|
|
+ :service-list="serviceList"
|
|
|
@remark-saved="getList"
|
|
@remark-saved="getList"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
@@ -144,19 +149,6 @@
|
|
|
@visible-change="handleBrandVisibleChange" />
|
|
@visible-change="handleBrandVisibleChange" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <el-col :span="12">
|
|
|
|
|
- <el-form-item label="所属区域">
|
|
|
|
|
- <el-cascader v-model="formAreaValue" :options="areaTreeOptions" placeholder="请选择区域"
|
|
|
|
|
- style="width: 100%" clearable @change="handleFormAreaChange" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="12">
|
|
|
|
|
- <el-form-item label="所属站点">
|
|
|
|
|
- <el-select v-model="form.stationId" style="width: 100%" filterable placeholder="请选择站点" clearable :disabled="!form.areaId">
|
|
|
|
|
- <el-option v-for="station in formStationList" :key="station.id" :label="station.name" :value="station.id" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </el-col>
|
|
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
<el-form-item label="姓名" required><el-input v-model="form.name" placeholder="请输入姓名" /></el-form-item>
|
|
<el-form-item label="姓名" required><el-input v-model="form.name" placeholder="请输入姓名" /></el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
@@ -170,6 +162,12 @@
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
|
|
+ <el-col :span="24">
|
|
|
|
|
+ <el-form-item label="所属站点">
|
|
|
|
|
+ <el-cascader v-model="formAreaValue" :options="areaTreeOptions" :props="{ value: 'id', label: 'name' }" placeholder="请选择站点"
|
|
|
|
|
+ style="width: 100%" clearable @change="handleFormAreaChange" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
|
|
|
<el-col :span="24"><div class="form-section-header">居住信息</div></el-col>
|
|
<el-col :span="24"><div class="form-section-header">居住信息</div></el-col>
|
|
|
<el-col :span="24">
|
|
<el-col :span="24">
|
|
@@ -243,6 +241,17 @@
|
|
|
</template>
|
|
</template>
|
|
|
</el-dialog>
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
+ <!-- Pet Remark Dialog -->
|
|
|
|
|
+ <el-dialog v-model="petRemarkDialogVisible" title="添加宠物备注" width="400px" append-to-body>
|
|
|
|
|
+ <el-input v-model="remarkForm.content" type="textarea" :rows="3" placeholder="请输入宠物备注内容..." />
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <span class="dialog-footer">
|
|
|
|
|
+ <el-button @click="petRemarkDialogVisible = false">取消</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="savePetRemark">保存</el-button>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
<!-- Full Add/Edit Pet Dialog -->
|
|
<!-- Full Add/Edit Pet Dialog -->
|
|
|
<el-dialog v-model="petDialogVisible" :title="petForm.id ? '编辑宠物' : '新增宠物'" width="800px">
|
|
<el-dialog v-model="petDialogVisible" :title="petForm.id ? '编辑宠物' : '新增宠物'" width="800px">
|
|
|
<el-tabs v-model="petDialogActiveTab">
|
|
<el-tabs v-model="petDialogActiveTab">
|
|
@@ -389,12 +398,16 @@ import { listCustomer, getCustomer, addCustomer, updateCustomer, delCustomer, ch
|
|
|
import { listAllTag } from '@/api/archieves/tag'
|
|
import { listAllTag } from '@/api/archieves/tag'
|
|
|
import { listPetByUser, addPet, updatePet, delPet } from '@/api/archieves/pet'
|
|
import { listPetByUser, addPet, updatePet, delPet } from '@/api/archieves/pet'
|
|
|
import { listAllChangeLog } from '@/api/archieves/changeLog'
|
|
import { listAllChangeLog } from '@/api/archieves/changeLog'
|
|
|
-import { listOnStore } from '@/api/system/areaStation'
|
|
|
|
|
|
|
+import { listAreaStation } from '@/api/system/areaStation'
|
|
|
import { listOnStore as listBrandOnStore } from '@/api/system/tenant'
|
|
import { listOnStore as listBrandOnStore } from '@/api/system/tenant'
|
|
|
import CustomerDetailDrawer from '@/components/CustomerDetailDrawer/index.vue'
|
|
import CustomerDetailDrawer from '@/components/CustomerDetailDrawer/index.vue'
|
|
|
import PetDetailDrawer from '@/components/PetDetailDrawer/index.vue'
|
|
import PetDetailDrawer from '@/components/PetDetailDrawer/index.vue'
|
|
|
|
|
+import { listAllService } from '@/api/service/list/index'
|
|
|
import { regionData, codeToText } from 'element-china-area-data'
|
|
import { regionData, codeToText } from 'element-china-area-data'
|
|
|
import PageSelect from '@/components/PageSelect/index.vue'
|
|
import PageSelect from '@/components/PageSelect/index.vue'
|
|
|
|
|
+import { useUserStore } from '@/store/modules/user'
|
|
|
|
|
+
|
|
|
|
|
+const userStore = useUserStore()
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance()
|
|
const { proxy } = getCurrentInstance()
|
|
|
const { sys_user_sex, sys_customer_status, sys_house_type, sys_entry_method, sys_pet_gender, sys_pet_size, sys_pet_type, sys_pet_breed } = toRefs(
|
|
const { sys_user_sex, sys_customer_status, sys_house_type, sys_entry_method, sys_pet_gender, sys_pet_size, sys_pet_type, sys_pet_breed } = toRefs(
|
|
@@ -406,32 +419,25 @@ const submitLoading = ref(false)
|
|
|
const total = ref(0)
|
|
const total = ref(0)
|
|
|
|
|
|
|
|
const allNodes = ref([])
|
|
const allNodes = ref([])
|
|
|
-const areaList = computed(() => allNodes.value.filter(n => n.type === 1))
|
|
|
|
|
-const filteredStationList = computed(() => {
|
|
|
|
|
- const areaId = searchForm.areaId
|
|
|
|
|
- const stations = allNodes.value.filter(n => n.type === 2)
|
|
|
|
|
- if (areaId) {
|
|
|
|
|
- return stations.filter(s => s.parentId === areaId)
|
|
|
|
|
- }
|
|
|
|
|
- return stations
|
|
|
|
|
-})
|
|
|
|
|
-const formStationList = computed(() => {
|
|
|
|
|
- const areaId = form.areaId
|
|
|
|
|
- const stations = allNodes.value.filter(n => n.type === 2)
|
|
|
|
|
- if (areaId) {
|
|
|
|
|
- return stations.filter(s => s.parentId === areaId)
|
|
|
|
|
- }
|
|
|
|
|
- return stations
|
|
|
|
|
-})
|
|
|
|
|
|
|
+// 废弃变量已清理,统一使用级联选择器展示全量树结构
|
|
|
|
|
|
|
|
const loadAreaStation = () => {
|
|
const loadAreaStation = () => {
|
|
|
- listOnStore().then((res) => {
|
|
|
|
|
|
|
+ listAreaStation().then((res) => {
|
|
|
allNodes.value = res.data || []
|
|
allNodes.value = res.data || []
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const onSearchAreaChange = () => {
|
|
|
|
|
- searchForm.stationId = undefined
|
|
|
|
|
|
|
+const searchAreaValue = ref([])
|
|
|
|
|
+const onSearchAreaChange = (value) => {
|
|
|
|
|
+ if (value && value.length > 0) {
|
|
|
|
|
+ const lastId = value[value.length - 1];
|
|
|
|
|
+ const node = allNodes.value.find(item => item.id === lastId);
|
|
|
|
|
+ searchForm.stationId = lastId;
|
|
|
|
|
+ searchForm.areaId = node ? node.parentId : undefined;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ searchForm.areaId = undefined;
|
|
|
|
|
+ searchForm.stationId = undefined;
|
|
|
|
|
+ }
|
|
|
handleSearch()
|
|
handleSearch()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -454,6 +460,7 @@ const dialogVisible = ref(false)
|
|
|
const drawerVisible = ref(false)
|
|
const drawerVisible = ref(false)
|
|
|
const petDrawerVisible = ref(false)
|
|
const petDrawerVisible = ref(false)
|
|
|
const remarkDialogVisible = ref(false)
|
|
const remarkDialogVisible = ref(false)
|
|
|
|
|
+const petRemarkDialogVisible = ref(false)
|
|
|
const petDialogVisible = ref(false)
|
|
const petDialogVisible = ref(false)
|
|
|
const isEdit = ref(false)
|
|
const isEdit = ref(false)
|
|
|
const detailActiveTab = ref('info')
|
|
const detailActiveTab = ref('info')
|
|
@@ -478,6 +485,13 @@ const formAreaValue = ref([])
|
|
|
const regionCascaderValue = ref([])
|
|
const regionCascaderValue = ref([])
|
|
|
|
|
|
|
|
const customerDetailRef = ref(null)
|
|
const customerDetailRef = ref(null)
|
|
|
|
|
+const serviceList = ref([])
|
|
|
|
|
+
|
|
|
|
|
+const getServiceList = () => {
|
|
|
|
|
+ listAllService().then((res) => {
|
|
|
|
|
+ serviceList.value = res.data || []
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const form = reactive({
|
|
const form = reactive({
|
|
|
id: undefined,
|
|
id: undefined,
|
|
@@ -544,21 +558,23 @@ const areaTreeOptions = computed(() => {
|
|
|
.filter(item => String(item.parentId) === String(parentId))
|
|
.filter(item => String(item.parentId) === String(parentId))
|
|
|
.map(item => {
|
|
.map(item => {
|
|
|
const children = buildTree(data, item.id)
|
|
const children = buildTree(data, item.id)
|
|
|
- const node = { value: item.id, label: item.name }
|
|
|
|
|
|
|
+ const node = { id: item.id, name: item.name }
|
|
|
if (children.length > 0) node.children = children
|
|
if (children.length > 0) node.children = children
|
|
|
return node
|
|
return node
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
- const areaData = allNodes.value.filter(n => n.type === 0 || n.type === 1)
|
|
|
|
|
- return buildTree(areaData, 0)
|
|
|
|
|
|
|
+ return buildTree(allNodes.value, 0)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const handleFormAreaChange = (value) => {
|
|
const handleFormAreaChange = (value) => {
|
|
|
- form.stationId = undefined
|
|
|
|
|
if (value && value.length > 0) {
|
|
if (value && value.length > 0) {
|
|
|
- form.areaId = value[value.length - 1]
|
|
|
|
|
|
|
+ const lastId = value[value.length - 1];
|
|
|
|
|
+ const node = allNodes.value.find(item => item.id === lastId);
|
|
|
|
|
+ form.stationId = lastId;
|
|
|
|
|
+ form.areaId = node ? node.parentId : undefined;
|
|
|
} else {
|
|
} else {
|
|
|
- form.areaId = undefined
|
|
|
|
|
|
|
+ form.stationId = undefined;
|
|
|
|
|
+ form.areaId = undefined;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -643,21 +659,20 @@ const handleEdit = (row) => {
|
|
|
})
|
|
})
|
|
|
userAvatarDisplayUrl.value = data.avatarUrl || ''
|
|
userAvatarDisplayUrl.value = data.avatarUrl || ''
|
|
|
// Restore area cascader value path
|
|
// Restore area cascader value path
|
|
|
- if (data.areaId) {
|
|
|
|
|
- const findPath = (nodes, targetId, path = []) => {
|
|
|
|
|
- for (const node of nodes) {
|
|
|
|
|
- const currentPath = [...path, node.id]
|
|
|
|
|
- if (node.id === targetId) return currentPath
|
|
|
|
|
- const children = allNodes.value.filter(n => (n.type === 0 || n.type === 1) && String(n.parentId) === String(node.id))
|
|
|
|
|
- if (children.length > 0) {
|
|
|
|
|
- const result = findPath(children, targetId, currentPath)
|
|
|
|
|
- if (result) return result
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (data.stationId || data.areaId) {
|
|
|
|
|
+ const targetId = data.stationId || data.areaId;
|
|
|
|
|
+ const path = [];
|
|
|
|
|
+ let currentId = targetId;
|
|
|
|
|
+ while (currentId && String(currentId) !== '0') {
|
|
|
|
|
+ path.unshift(currentId);
|
|
|
|
|
+ const currentArea = allNodes.value.find((item) => String(item.id) === String(currentId));
|
|
|
|
|
+ if (currentArea) {
|
|
|
|
|
+ currentId = currentArea.parentId;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
- return null
|
|
|
|
|
}
|
|
}
|
|
|
- const roots = allNodes.value.filter(n => (n.type === 0 || n.type === 1) && String(n.parentId) === '0')
|
|
|
|
|
- formAreaValue.value = findPath(roots, data.areaId) || []
|
|
|
|
|
|
|
+ formAreaValue.value = path;
|
|
|
} else {
|
|
} else {
|
|
|
formAreaValue.value = []
|
|
formAreaValue.value = []
|
|
|
}
|
|
}
|
|
@@ -728,6 +743,23 @@ const saveRemark = () => {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const savePetRemark = () => {
|
|
|
|
|
+ if (!remarkForm.content) return ElMessage.warning('请输入内容')
|
|
|
|
|
+ const data = {
|
|
|
|
|
+ id: currentPet.value.id,
|
|
|
|
|
+ remark: remarkForm.content
|
|
|
|
|
+ }
|
|
|
|
|
+ updatePet(data).then(() => {
|
|
|
|
|
+ ElMessage.success('宠物备注添加成功')
|
|
|
|
|
+ petRemarkDialogVisible.value = false
|
|
|
|
|
+ if (drawerVisible.value) {
|
|
|
|
|
+ loadDetailPets(currentUser.value.id)
|
|
|
|
|
+ loadDetailLogs(currentUser.value.id, 'customer')
|
|
|
|
|
+ }
|
|
|
|
|
+ getList()
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const saveUser = () => {
|
|
const saveUser = () => {
|
|
|
if (!form.name) return ElMessage.warning('请输入姓名')
|
|
if (!form.name) return ElMessage.warning('请输入姓名')
|
|
|
if (!form.phone) return ElMessage.warning('请输入电话')
|
|
if (!form.phone) return ElMessage.warning('请输入电话')
|
|
@@ -893,7 +925,8 @@ const handlePetEdit = (row) => {
|
|
|
|
|
|
|
|
const handlePetRemark = (row) => {
|
|
const handlePetRemark = (row) => {
|
|
|
currentPet.value = row
|
|
currentPet.value = row
|
|
|
- petDrawerVisible.value = true
|
|
|
|
|
|
|
+ remarkForm.content = ''
|
|
|
|
|
+ petRemarkDialogVisible.value = true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handlePetDelete = (row) => {
|
|
const handlePetDelete = (row) => {
|
|
@@ -926,6 +959,7 @@ onMounted(() => {
|
|
|
loadTags()
|
|
loadTags()
|
|
|
loadAreaStation()
|
|
loadAreaStation()
|
|
|
getBrandList()
|
|
getBrandList()
|
|
|
|
|
+ getServiceList()
|
|
|
})
|
|
})
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|