| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- <template>
- <view class="user-edit-page">
- <NavBar title="编辑用户" bgColor="#fff" color="#000"></NavBar>
- <!-- 基本资料 -->
- <view class="section-title">基本资料</view>
- <view class="form-card">
- <view class="form-item">
- <text class="form-label require">姓名</text>
- <input class="form-input" v-model="form.name" placeholder="请输入姓名" />
- </view>
- <view class="form-item">
- <text class="form-label require">手机号</text>
- <input class="form-input" v-model="form.phone" type="number" placeholder="请输入手机号" />
- </view>
- <view class="form-item">
- <text class="form-label">性别</text>
- <picker :range="genderOptions" range-key="label" @change="onGenderChange">
- <view class="picker-value" :class="{'placeholder': form.gender === undefined}">
- {{ getGenderLabel }}
- </view>
- </picker>
- </view>
- </view>
- <!-- 居住信息 -->
- <view class="section-title">居住信息</view>
- <view class="form-card">
- <view class="form-item">
- <text class="form-label require">所属站点</text>
- <picker mode="multiSelector" :range="stationOptions" range-key="name" @change="onStationChange" @columnchange="onStationColumnChange" :value="stationIndex">
- <view class="picker-value" :class="{'placeholder': form.stationId === undefined}">
- {{ getStationLabel }}
- </view>
- </picker>
- </view>
- <view class="form-item">
- <text class="form-label require">详细住址</text>
- <input class="form-input" v-model="form.address" placeholder="请输入街道/门牌号" />
- </view>
- <view class="form-item">
- <text class="form-label">房屋类型</text>
- <picker :range="houseTypeOptions" range-key="label" @change="onHouseTypeChange">
- <view class="picker-value" :class="{'placeholder': !form.houseType}">
- {{ getHouseTypeLabel }}
- </view>
- </picker>
- </view>
- <view class="form-item">
- <text class="form-label require">入门方式</text>
- <picker :range="entryMethodOptions" range-key="label" @change="onEntryMethodChange">
- <view class="picker-value" :class="{'placeholder': !form.entryMethod}">
- {{ getEntryMethodLabel }}
- </view>
- </picker>
- </view>
- <view class="form-item" v-if="form.entryMethod === 'password'">
- <text class="form-label require">开门密码</text>
- <input class="form-input" v-model="form.entryPassword" placeholder="请输入密码" />
- </view>
- <view class="form-item" v-if="form.entryMethod === 'key'">
- <text class="form-label require">钥匙位置</text>
- <input class="form-input" v-model="form.keyLocation" placeholder="如:地毯下" />
- </view>
- </view>
- <!-- 其他 -->
- <view class="section-title">其他</view>
- <view class="form-card">
- <view class="form-item vertical">
- <text class="form-label">备注说明</text>
- <textarea class="form-textarea" v-model="form.remark" placeholder="请输入备注" />
- </view>
- </view>
- <!-- 底部固定操作栏 -->
- <view class="footer-bar">
- <button class="save-btn" :loading="saving" @click="onSave">保存修改</button>
- </view>
- </view>
- </template>
- <script setup>
- // @Author: Antigravity
- import { ref, reactive, computed } from 'vue'
- import { onLoad } from '@dcloudio/uni-app'
- import NavBar from '@/components/nav-bar/index.vue'
- import { getCustomer, updateCustomer } from '@/api/archieves/customer'
- import { listAreaStation } from '@/api/system/areaStation'
- import customerEnums from '@/json/customer.json'
- const { houseTypeOptions, entryMethodOptions } = customerEnums
- const genderOptions = [{ label: '男', value: 0 }, { label: '女', value: 1 }]
- const saving = ref(false)
- const stationOptions = ref([[], [], []])
- const stationIndex = ref([0, 0, 0])
- const allStationNodes = ref([])
- const form = reactive({
- id: undefined,
- name: '',
- phone: '',
- gender: undefined,
- areaId: undefined,
- stationId: undefined,
- address: '',
- houseType: '',
- entryMethod: '',
- entryPassword: '',
- keyLocation: '',
- remark: ''
- })
- onLoad(async (options) => {
- try {
- uni.showLoading({ title: '加载中...' })
- // 1. 获取站点数据
- const stationRes = await listAreaStation()
- allStationNodes.value = Array.isArray(stationRes) ? stationRes : (stationRes?.data || [])
-
- // 2. 如果是编辑,获取用户详情
- if (options.id) {
- const res = await getCustomer(options.id)
- if (res) {
- Object.assign(form, res)
- }
- }
-
- // 3. 初始化选择器并尝试回定位
- initStationPicker()
- } catch(err) {
- console.error(err)
- } finally {
- uni.hideLoading()
- }
- })
- // 初始化三级联动
- const initStationPicker = () => {
- const nodes = allStationNodes.value
- if (nodes.length === 0) return
- const cities = nodes.filter(n => String(n.parentId) === '0')
-
- // 如果已有 stationId,尝试找索引
- let cIdx = 0, aIdx = 0, sIdx = 0
- if (form.stationId) {
- const targetStation = nodes.find(n => String(n.id) === String(form.stationId))
- if (targetStation) {
- const targetArea = nodes.find(n => String(n.id) === String(targetStation.parentId))
- if (targetArea) {
- cIdx = cities.findIndex(n => String(n.id) === String(targetArea.parentId))
- cIdx = cIdx === -1 ? 0 : cIdx
-
- const areas = nodes.filter(n => String(n.parentId) === String(cities[cIdx].id))
- aIdx = areas.findIndex(n => String(n.id) === String(targetArea.id))
- aIdx = aIdx === -1 ? 0 : aIdx
-
- const stations = nodes.filter(n => String(n.parentId) === String(areas[aIdx].id))
- sIdx = stations.findIndex(n => String(n.id) === String(targetStation.id))
- sIdx = sIdx === -1 ? 0 : sIdx
-
- stationOptions.value = [cities, areas, stations]
- stationIndex.value = [cIdx, aIdx, sIdx]
- return
- }
- }
- }
- // 默认初始化
- const areas = nodes.filter(n => String(n.parentId) === String(cities[0]?.id || ''))
- const stations = areas.length > 0 ? nodes.filter(n => String(n.parentId) === String(areas[0].id)) : []
- stationOptions.value = [cities, areas, stations]
- stationIndex.value = [0, 0, 0]
- }
- const onStationColumnChange = (e) => {
- const column = e.detail.column
- const value = e.detail.value
- stationIndex.value[column] = value
- const nodes = allStationNodes.value
- if (column === 0) {
- const selectedCity = stationOptions.value[0][value]
- const newAreas = selectedCity ? nodes.filter(n => String(n.parentId) === String(selectedCity.id)) : []
- stationOptions.value[1] = newAreas
- stationOptions.value[2] = newAreas.length > 0 ? nodes.filter(n => String(n.parentId) === String(newAreas[0].id)) : []
- stationIndex.value[1] = 0
- stationIndex.value[2] = 0
- } else if (column === 1) {
- const selectedArea = stationOptions.value[1][value]
- const newStations = selectedArea ? nodes.filter(n => String(n.parentId) === String(selectedArea.id)) : []
- stationOptions.value[2] = newStations
- stationIndex.value[2] = 0
- }
- }
- const onStationChange = (e) => {
- stationIndex.value = e.detail.value
- const stations = stationOptions.value[2]
- const selectedStation = stations[stationIndex.value[2]]
- if (selectedStation && String(selectedStation.type) === '2') {
- form.stationId = selectedStation.id
- form.areaId = selectedStation.parentId
- } else {
- uni.showToast({ title: '请选择到具体的站点层级', icon: 'none' })
- }
- }
- const getGenderLabel = computed(() => genderOptions.find(o => o.value === form.gender)?.label || '请选择')
- const getHouseTypeLabel = computed(() => houseTypeOptions.find(o => o.value === form.houseType)?.label || '请选择')
- const getEntryMethodLabel = computed(() => entryMethodOptions.find(o => o.value === form.entryMethod)?.label || '请选择')
- const getStationLabel = computed(() => {
- if (!form.stationId) return '请选择'
- const city = stationOptions.value[0][stationIndex.value[0]]?.name || ''
- const area = stationOptions.value[1][stationIndex.value[1]]?.name || ''
- const station = stationOptions.value[2][stationIndex.value[2]]?.name || ''
- return `${city} - ${area} - ${station}`
- })
- const onGenderChange = (e) => { form.gender = genderOptions[e.detail.value].value }
- const onHouseTypeChange = (e) => { form.houseType = houseTypeOptions[e.detail.value].value }
- const onEntryMethodChange = (e) => {
- form.entryMethod = entryMethodOptions[e.detail.value].value
- form.entryPassword = ''
- form.keyLocation = ''
- }
- const onSave = async () => {
- if (!form.name) return uni.showToast({ title: '请输入姓名', icon: 'none' })
- if (!form.phone) return uni.showToast({ title: '请输入手机号', icon: 'none' })
- if (!form.stationId) return uni.showToast({ title: '请选择所属站点', icon: 'none' })
- if (!form.address) return uni.showToast({ title: '请输入详细住址', icon: 'none' })
- if (!form.entryMethod) return uni.showToast({ title: '请选择入门方式', icon: 'none' })
-
- saving.value = true
- try {
- await updateCustomer(form)
- uni.showToast({ title: '保存成功', icon: 'success' })
- setTimeout(() => uni.navigateBack(), 1000)
- } catch(err) {
- } finally {
- saving.value = false
- }
- }
- </script>
- <style lang="scss" scoped>
- .user-edit-page { min-height: 100vh; background: #f7f8fa; padding-bottom: calc(140rpx + env(safe-area-inset-bottom)); }
- .section-title { font-size: 28rpx; font-weight: bold; color: #666; padding: 24rpx 32rpx 12rpx; }
- .form-card { background: #fff; border-radius: 24rpx; padding: 8rpx 32rpx; margin: 0 24rpx 24rpx; }
- .form-item { display: flex; align-items: center; padding: 28rpx 0; border-bottom: 1rpx solid #f5f5f5; }
- .form-item:last-child { border-bottom: none; }
- .form-item.vertical { flex-direction: column; align-items: flex-start; }
- .form-item.vertical .form-textarea { width: 100%; height: 160rpx; margin-top: 16rpx; background: #f9f9f9; padding: 16rpx; box-sizing: border-box; border-radius: 12rpx; font-size: 28rpx; }
- .form-label { width: 200rpx; font-size: 28rpx; color: #333; flex-shrink: 0; }
- .form-label.require::before { content: '*'; color: #f56c6c; margin-right: 4rpx; }
- .form-input { flex: 1; font-size: 28rpx; color: #333; text-align: right; }
- .picker-value { flex: 1; font-size: 28rpx; color: #333; text-align: right; min-height: 40rpx; }
- .picker-value.placeholder { color: #ccc; }
- .footer-bar { position: fixed; bottom: 0; left: 0; right: 0; background: #fff; padding: 20rpx 32rpx calc(20rpx + env(safe-area-inset-bottom)); box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.05); z-index: 100; }
- .save-btn { width: 100%; height: 88rpx; background: linear-gradient(90deg, #ffd53f, #ff9500); color: #333; border: none; border-radius: 44rpx; font-size: 32rpx; font-weight: bold; line-height: 88rpx; }
- </style>
|