增强办公,新增审批,缺少电子签章功能
This commit is contained in:
@@ -2,151 +2,171 @@
|
||||
<view v-if="visible" class="picker-mask" @tap="close">
|
||||
<view class="picker-sheet" @tap.stop>
|
||||
<view class="picker-header">
|
||||
<text class="picker-title">选择目的地</text>
|
||||
<text class="picker-title">选择城市</text>
|
||||
<text class="picker-close" @tap="close">关闭</text>
|
||||
</view>
|
||||
|
||||
<view class="city-path">{{ currentPath }}</view>
|
||||
<view class="tip-box">
|
||||
<text class="tip-main">仅精确到城市即可</text>
|
||||
<text class="tip-sub">如果列表里没有想要的城市,可以先在上方补录后再选择。</text>
|
||||
</view>
|
||||
|
||||
<picker-view class="city-wheel" :value="wheelValue" @change="onChange">
|
||||
<picker-view-column>
|
||||
<view class="wheel-item" v-for="item in continents" :key="item.name">
|
||||
<view class="wheel-value">{{ item.name }}</view>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view class="wheel-item" v-for="item in currentCountries" :key="item.name">
|
||||
<view class="wheel-value">{{ item.name }}</view>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view class="wheel-item" v-for="item in currentCities" :key="item.name">
|
||||
<view class="wheel-value">{{ item.name }}</view>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
<view class="add-box">
|
||||
<input v-model="newCountryName" class="add-input" type="text" placeholder="补录国家" />
|
||||
<input v-model="newCityName" class="add-input" type="text" placeholder="补录城市" />
|
||||
<button class="add-btn" :loading="adding" @tap="addNewCity">补录并新增</button>
|
||||
</view>
|
||||
|
||||
<view class="city-path">{{ currentCountry && currentCity ? (currentCountry + ' / ' + currentCity) : '请选择城市' }}</view>
|
||||
|
||||
<view class="selector-wrap">
|
||||
<picker-view class="picker-view" :value="pickerValue" @change="onPickerChange">
|
||||
<picker-view-column>
|
||||
<view v-for="item in countryOptions" :key="item.name" class="picker-item">{{ item.name }}</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view v-for="item in currentCities" :key="item.cityId || item.cityName" class="picker-item">{{ item.cityName }}</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
|
||||
<view class="picker-footer">
|
||||
<button class="btn ghost" @tap="close">取消</button>
|
||||
<button class="btn primary" @tap="confirm">确定</button>
|
||||
<button class="btn primary" :disabled="!currentCity" @tap="confirm">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const CITY_DATA = [
|
||||
{ name: '亚洲', countries: [
|
||||
{ name: '中国', cities: ['北京', '上海', '广州', '深圳', '杭州', '成都', '重庆', '西安', '南京', '武汉'] },
|
||||
{ name: '日本', cities: ['东京', '大阪', '京都', '名古屋', '横滨'] },
|
||||
{ name: '韩国', cities: ['首尔', '釜山', '仁川'] },
|
||||
{ name: '新加坡', cities: ['新加坡'] },
|
||||
{ name: '泰国', cities: ['曼谷', '清迈', '普吉'] },
|
||||
{ name: '马来西亚', cities: ['吉隆坡', '槟城', '新山'] },
|
||||
{ name: '印度', cities: ['新德里', '孟买', '班加罗尔'] },
|
||||
{ name: '阿联酋', cities: ['迪拜', '阿布扎比'] }
|
||||
]},
|
||||
{ name: '欧洲', countries: [
|
||||
{ name: '英国', cities: ['伦敦', '曼彻斯特', '伯明翰'] },
|
||||
{ name: '法国', cities: ['巴黎', '里昂', '马赛'] },
|
||||
{ name: '德国', cities: ['柏林', '慕尼黑', '法兰克福'] },
|
||||
{ name: '意大利', cities: ['罗马', '米兰', '威尼斯'] },
|
||||
{ name: '西班牙', cities: ['马德里', '巴塞罗那', '瓦伦西亚'] },
|
||||
{ name: '荷兰', cities: ['阿姆斯特丹', '鹿特丹'] }
|
||||
]},
|
||||
{ name: '北美洲', countries: [
|
||||
{ name: '美国', cities: ['纽约', '洛杉矶', '旧金山', '芝加哥', '西雅图'] },
|
||||
{ name: '加拿大', cities: ['多伦多', '温哥华', '蒙特利尔'] },
|
||||
{ name: '墨西哥', cities: ['墨西哥城', '瓜达拉哈拉'] }
|
||||
]},
|
||||
{ name: '南美洲', countries: [
|
||||
{ name: '巴西', cities: ['圣保罗', '里约热内卢', '巴西利亚'] },
|
||||
{ name: '阿根廷', cities: ['布宜诺斯艾利斯', '科尔多瓦'] },
|
||||
{ name: '智利', cities: ['圣地亚哥'] }
|
||||
]},
|
||||
{ name: '大洋洲', countries: [
|
||||
{ name: '澳大利亚', cities: ['悉尼', '墨尔本', '布里斯班', '珀斯'] },
|
||||
{ name: '新西兰', cities: ['奥克兰', '惠灵顿', '基督城'] }
|
||||
]},
|
||||
{ name: '非洲', countries: [
|
||||
{ name: '埃及', cities: ['开罗', '亚历山大'] },
|
||||
{ name: '南非', cities: ['约翰内斯堡', '开普敦'] },
|
||||
{ name: '肯尼亚', cities: ['内罗毕'] }
|
||||
]}
|
||||
]
|
||||
import { listCities, addCity } from '@/api/fad/city'
|
||||
|
||||
export default {
|
||||
props: { visible: Boolean, value: String },
|
||||
emits: ['update:visible', 'change'],
|
||||
data() {
|
||||
return {
|
||||
wheelValue: [0, 0, 0],
|
||||
data: CITY_DATA
|
||||
loading: false,
|
||||
adding: false,
|
||||
countryOptions: [],
|
||||
cityMap: {},
|
||||
pickerValue: [0, 0],
|
||||
currentCountry: '',
|
||||
currentCity: '',
|
||||
newCountryName: '',
|
||||
newCityName: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
continents() { return this.data },
|
||||
currentCountries() { return this.continents[this.wheelValue[0]]?.countries || [] },
|
||||
currentCities() { return this.currentCountries[this.wheelValue[1]]?.cities?.map(name => ({ name })) || [] },
|
||||
currentPath() {
|
||||
const c1 = this.continents[this.wheelValue[0]]?.name || ''
|
||||
const c2 = this.currentCountries[this.wheelValue[1]]?.name || ''
|
||||
const c3 = this.currentCities[this.wheelValue[2]]?.name || ''
|
||||
return [c1, c2, c3].filter(Boolean).join(' / ') || '请选择目的地'
|
||||
currentCities() {
|
||||
return this.cityMap[this.currentCountry] || []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val) this.initFromValue()
|
||||
},
|
||||
wheelValue() {
|
||||
this.normalize()
|
||||
visible(v) {
|
||||
if (v) this.initValue()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initFromValue() {
|
||||
const target = String(this.value || '')
|
||||
let found = [0, 0, 0]
|
||||
this.data.forEach((continent, i) => {
|
||||
continent.countries.forEach((country, j) => {
|
||||
country.cities.forEach((city, k) => {
|
||||
const full = `${continent.name}/${country.name}/${city}`
|
||||
if (target === full || target === city) found = [i, j, k]
|
||||
})
|
||||
async initValue() {
|
||||
this.currentCountry = ''
|
||||
this.currentCity = ''
|
||||
this.newCountryName = ''
|
||||
this.newCityName = ''
|
||||
await this.loadOptions()
|
||||
this.applyDefaultValue()
|
||||
},
|
||||
async loadOptions() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await listCities({ pageNum: 1, pageSize: 1000 })
|
||||
const rows = res && (res.rows || (res.data && res.data.rows) || res.data || [])
|
||||
const map = {}
|
||||
const countries = []
|
||||
rows.forEach(item => {
|
||||
const country = item.countryName || ''
|
||||
const city = item.cityName || ''
|
||||
if (!country || !city) return
|
||||
if (!map[country]) {
|
||||
map[country] = []
|
||||
countries.push({ name: country })
|
||||
}
|
||||
map[country].push(item)
|
||||
})
|
||||
})
|
||||
this.wheelValue = found
|
||||
},
|
||||
normalize() {
|
||||
const c = this.continents[this.wheelValue[0]]
|
||||
if (!c) return
|
||||
if (this.wheelValue[1] >= c.countries.length) this.wheelValue[1] = 0
|
||||
const country = c.countries[this.wheelValue[1]]
|
||||
if (!country) return
|
||||
if (this.wheelValue[2] >= country.cities.length) this.wheelValue[2] = 0
|
||||
},
|
||||
onChange(e) {
|
||||
this.wheelValue = (e.detail.value || []).map(v => Number(v))
|
||||
},
|
||||
confirm() {
|
||||
const continent = this.continents[this.wheelValue[0]]
|
||||
const country = this.currentCountries[this.wheelValue[1]]
|
||||
const city = this.currentCities[this.wheelValue[2]]
|
||||
const payload = {
|
||||
continent: continent?.name || '',
|
||||
country: country?.name || '',
|
||||
city: city?.name || '',
|
||||
label: [continent?.name, country?.name, city?.name].filter(Boolean).join(' / '),
|
||||
value: [continent?.name, country?.name, city?.name].filter(Boolean).join('/')
|
||||
this.countryOptions = countries
|
||||
this.cityMap = map
|
||||
} catch (e) {
|
||||
uni.showToast({ title: e.message || '加载城市失败', icon: 'none' })
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
this.$emit('change', payload)
|
||||
},
|
||||
applyDefaultValue() {
|
||||
const value = String(this.value || '')
|
||||
for (let i = 0; i < this.countryOptions.length; i++) {
|
||||
const country = this.countryOptions[i].name
|
||||
const cityList = this.cityMap[country] || []
|
||||
const j = cityList.findIndex(item => item.cityName === value)
|
||||
if (j >= 0) {
|
||||
this.pickerValue = [i, j]
|
||||
this.currentCountry = country
|
||||
this.currentCity = cityList[j].cityName
|
||||
return
|
||||
}
|
||||
}
|
||||
if (this.countryOptions.length) {
|
||||
this.currentCountry = this.countryOptions[0].name
|
||||
this.currentCity = (this.cityMap[this.currentCountry] || [])[0]?.cityName || ''
|
||||
this.pickerValue = [0, 0]
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
close() { this.$emit('update:visible', false) }
|
||||
onPickerChange(e) {
|
||||
const value = (e.detail.value || []).map(v => Number(v))
|
||||
const countryIndex = value[0] || 0
|
||||
const country = this.countryOptions[countryIndex]?.name || ''
|
||||
const cities = this.cityMap[country] || []
|
||||
const cityIndex = Math.min(value[1] || 0, Math.max(cities.length - 1, 0))
|
||||
this.pickerValue = [countryIndex, cityIndex]
|
||||
this.currentCountry = country
|
||||
this.currentCity = cities[cityIndex]?.cityName || ''
|
||||
},
|
||||
async addNewCity() {
|
||||
const countryName = String(this.newCountryName || '').trim()
|
||||
const cityName = String(this.newCityName || '').trim()
|
||||
if (!countryName || !cityName) {
|
||||
uni.showToast({ title: '请填写国家和城市', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.adding = true
|
||||
try {
|
||||
await addCity({ countryName, cityName, status: 1, remark: '补录城市' })
|
||||
uni.showToast({ title: '补录成功', icon: 'success' })
|
||||
this.newCountryName = ''
|
||||
this.newCityName = ''
|
||||
await this.loadOptions()
|
||||
this.currentCountry = countryName
|
||||
this.currentCity = cityName
|
||||
const countryIndex = this.countryOptions.findIndex(item => item.name === countryName)
|
||||
const cityIndex = (this.cityMap[countryName] || []).findIndex(item => item.cityName === cityName)
|
||||
this.pickerValue = [Math.max(countryIndex, 0), Math.max(cityIndex, 0)]
|
||||
} catch (e) {
|
||||
uni.showToast({ title: e.message || '补录失败', icon: 'none' })
|
||||
} finally {
|
||||
this.adding = false
|
||||
}
|
||||
},
|
||||
confirm() {
|
||||
if (!this.currentCity) return
|
||||
this.$emit('change', this.currentCity)
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.picker-mask{position:fixed;inset:0;background:rgba(15,23,42,.45);z-index:1000;display:flex;align-items:flex-end}.picker-sheet{width:100%;background:#fff;border-radius:24rpx 24rpx 0 0;padding:20rpx;box-sizing:border-box}.picker-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16rpx}.picker-title{font-size:30rpx;font-weight:800;color:#111827}.picker-close{font-size:26rpx;color:#1677ff}.city-path{padding:12rpx 16rpx;margin-bottom:12rpx;border-radius:16rpx;background:#f8fafc;color:#334155;font-size:24rpx}.city-wheel{height:520rpx;display:flex}.wheel-item{display:flex;align-items:center;justify-content:center;height:88rpx}.wheel-value{font-size:28rpx;font-weight:600;color:#111827}.picker-footer{display:flex;gap:12rpx;margin-top:16rpx}.btn{flex:1;border-radius:16rpx}.btn.ghost{background:#f3f4f6;color:#334155}.btn.primary{background:linear-gradient(135deg,#1677ff,#4f9dff);color:#fff}
|
||||
</style>
|
||||
.picker-mask{position:fixed;inset:0;background:rgba(15,23,42,.45);z-index:1000;display:flex;align-items:flex-end}.picker-sheet{width:100%;background:#fff;border-radius:24rpx 24rpx 0 0;padding:20rpx;box-sizing:border-box}.picker-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16rpx}.picker-title{font-size:30rpx;font-weight:800;color:#111827}.picker-close{font-size:26rpx;color:#1677ff}.tip-box{padding:12rpx 16rpx;border-radius:16rpx;background:#eff6ff;margin-bottom:12rpx}.tip-main{display:block;font-size:26rpx;font-weight:700;color:#1d4ed8}.tip-sub{display:block;margin-top:6rpx;font-size:22rpx;color:#1e40af;line-height:1.5}.add-box{display:flex;gap:10rpx;flex-wrap:wrap;margin-bottom:12rpx}.add-input{flex:1;min-width:240rpx;height:72rpx;padding:0 16rpx;border:1px solid #e5e7eb;border-radius:16rpx;background:#fff}.add-btn{width:100%;height:72rpx;line-height:72rpx;border-radius:16rpx;background:#10b981;color:#fff;font-size:26rpx}.city-path{padding:12rpx 16rpx;margin-bottom:12rpx;border-radius:16rpx;background:#f8fafc;color:#334155;font-size:24rpx;line-height:1.5}.selector-wrap{height:420rpx;border:1px solid #eef2f7;border-radius:16rpx;overflow:hidden}.picker-view{height:100%}.picker-item{height:84rpx;line-height:84rpx;text-align:center;font-size:28rpx;color:#111827}.picker-footer{display:flex;gap:12rpx;margin-top:16rpx}.btn{flex:1;border-radius:16rpx}.btn.ghost{background:#f3f4f6;color:#334155}.btn.primary{background:linear-gradient(135deg,#1677ff,#4f9dff);color:#fff}.btn.primary[disabled]{background:#9cc2ff;color:#fff}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user