mirror of
https://gitee.com/niucloud-team/niucloud-admin.git
synced 2025-12-11 18:32:49 +00:00
update uni-app
This commit is contained in:
parent
0d7359e222
commit
1c0d75aae0
@ -82,4 +82,4 @@
|
||||
"vue-tsc": "^1.0.24",
|
||||
"windicss": "^3.5.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,4 +166,50 @@ export function getCollect(data: AnyObject){
|
||||
*/
|
||||
export function deleteCollect(id: number){
|
||||
return request.delete(`member/collect/${id}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员收货地址列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getAddressList(params: Record<string, any>) {
|
||||
return request.get(`member/address`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员收货地址详情
|
||||
* @param id 会员收货地址id
|
||||
* @returns
|
||||
*/
|
||||
export function getAddressInfo(id: number) {
|
||||
return request.get(`member/address/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加会员收货地址
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function addAddress(params: Record<string, any>) {
|
||||
return request.post('member/address', params, { showErrorMessage: true, showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑会员收货地址
|
||||
* @param id
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function editAddress(params: Record<string, any>) {
|
||||
return request.put(`member/address/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除会员收货地址
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
export function deleteAddress(id: number) {
|
||||
return request.delete(`member/address/${id}`, { showErrorMessage: true, showSuccessMessage: true })
|
||||
}
|
||||
|
||||
@ -82,4 +82,28 @@ export function getWeappTemplateId(keys : string) {
|
||||
*/
|
||||
export function getWapIndexList(data : AnyObject) {
|
||||
return request.get('wap_index', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下级地址列表
|
||||
* @param pid
|
||||
*/
|
||||
export function getAreaListByPid(pid: number = 0) {
|
||||
return request.get(`area/list_by_pid/${pid}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取地址树列表
|
||||
* @param level
|
||||
*/
|
||||
export function getAreatree(level: number = 1) {
|
||||
return request.get(`area/tree/${level}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取地址
|
||||
* @param code
|
||||
*/
|
||||
export function getAreaByCode(code: number | string) {
|
||||
return request.get(`area/code/${code}`)
|
||||
}
|
||||
122
uni-app/src/app/components/diy/goods-list/index.vue
Normal file
122
uni-app/src/app/components/diy/goods-list/index.vue
Normal file
@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<view :style="warpCss">
|
||||
<div class="flex flex-wrap justify-between">
|
||||
<view class="w-[332rpx] bg-[#fff] box-border rounded-[10rpx] mb-[24rpx] overflow-hidden">
|
||||
<u--image width="332rpx" height="332rpx" src="" model="aspectFill">
|
||||
<template #error>
|
||||
<u-icon name="photo" color="#999" size="50"></u-icon>
|
||||
</template>
|
||||
</u--image>
|
||||
<view class="px-[16rpx] mt-[18rpx] h-[80rpx] leading-[40rpx] text-[28rpx] font-bold multi-hidden">
|
||||
山茶花保湿美白淡斑补水乳护肤化妆品补水更高效
|
||||
</view>
|
||||
<view class="px-[16rpx] pb-[20rpx] flex justify-between items-end mt-[12rpx]" >
|
||||
<text class="text-[28rpx] font-bold text-[#FF3223]">¥100.00</text>
|
||||
<text class="text-[22rpx] text-[#888] leading-[31rpx]">已售100件</text>
|
||||
</view>
|
||||
</view>
|
||||
</div>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 应用列表
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { redirect, img } from '@/utils/common';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import { getWapIndexList } from '@/app/api/system';
|
||||
|
||||
const props = defineProps(['component', 'index', 'pullDownRefresh']);
|
||||
const diyStore = useDiyStore();
|
||||
|
||||
const diyComponent = computed(() => {
|
||||
if (diyStore.mode == 'decorate') {
|
||||
return diyStore.value[props.index];
|
||||
} else {
|
||||
return props.component;
|
||||
}
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
if (diyComponent.value.componentBgColor) style += 'background-color:' + diyComponent.value.componentBgColor + ';';
|
||||
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
|
||||
return style;
|
||||
})
|
||||
|
||||
const list = ref([])
|
||||
|
||||
watch(
|
||||
() => props.pullDownRefresh,
|
||||
(newValue, oldValue) => {
|
||||
// 处理下拉刷新业务
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
// 装修模式下刷新
|
||||
if (diyStore.mode == 'decorate') {
|
||||
watch(
|
||||
() => diyComponent.value,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue && newValue.componentName == 'AddonList') {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
const refresh = () => {
|
||||
// 装修模式下设置默认图
|
||||
if (diyStore.mode == 'decorate') {
|
||||
diyComponent.value.list.forEach((item : any, index) => {
|
||||
if (item.icon == '') {
|
||||
item.icon = 'static/resource/images/diy/figure.png';
|
||||
}
|
||||
if (item.title == '') {
|
||||
item.title = '应用名称';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (diyComponent.value.list.length == 0) {
|
||||
getWapIndexList().then((res) => {
|
||||
list.value = res.data;
|
||||
})
|
||||
} else {
|
||||
list.value = diyComponent.value.list;
|
||||
}
|
||||
}
|
||||
|
||||
const toLink = (url : string) => {
|
||||
redirect({ url })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 单行超出隐藏 */
|
||||
.using-hidden {
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
/* 多行超出隐藏 */
|
||||
.multi-hidden {
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
</style>
|
||||
@ -27,6 +27,39 @@
|
||||
<template v-if="component.componentName == 'Text'">
|
||||
<diy-text :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-text>
|
||||
</template>
|
||||
<template v-if="component.componentName == 'TourismHotel'">
|
||||
<diy-tourism-hotel :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-tourism-hotel>
|
||||
</template>
|
||||
<template v-if="component.componentName == 'TourismTickets'">
|
||||
<diy-tourism-tickets :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-tourism-tickets>
|
||||
</template>
|
||||
<template v-if="component.componentName == 'TourismTravel'">
|
||||
<diy-tourism-travel :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-tourism-travel>
|
||||
</template>
|
||||
<template v-if="component.componentName == 'TourismHotel'">
|
||||
<diy-tourism-hotel :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-tourism-hotel>
|
||||
</template>
|
||||
<template v-if="component.componentName == 'TourismTickets'">
|
||||
<diy-tourism-tickets :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-tourism-tickets>
|
||||
</template>
|
||||
<template v-if="component.componentName == 'TourismTravel'">
|
||||
<diy-tourism-travel :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-tourism-travel>
|
||||
</template>
|
||||
<template v-if="component.componentName == 'VipcardCard'">
|
||||
<diy-vipcard-card :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-vipcard-card>
|
||||
</template>
|
||||
<template v-if="component.componentName == 'VipcardReserve'">
|
||||
<diy-vipcard-reserve :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-vipcard-reserve>
|
||||
</template>
|
||||
<template v-if="component.componentName == 'VipcardStore'">
|
||||
<diy-vipcard-store :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-vipcard-store>
|
||||
</template>
|
||||
<template v-if="component.componentName == 'GoodsList'">
|
||||
<diy-goods-list :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-goods-list>
|
||||
</template>
|
||||
<template v-if="component.componentName == 'Notice'">
|
||||
<diy-notice :component="component" :index="index" :pullDownRefresh="props.pullDownRefresh"></diy-notice>
|
||||
</template>
|
||||
</view>
|
||||
<template v-if="diyStore.mode == '' && data.global.bottomTabBarSwitch">
|
||||
<view class="pt-[20rpx]"></view>
|
||||
@ -35,6 +68,12 @@
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import diyTourismHotel from '@/tourism/components/diy/tourism-hotel/index.vue';
|
||||
import diyTourismTickets from '@/tourism/components/diy/tourism-tickets/index.vue';
|
||||
import diyTourismTravel from '@/tourism/components/diy/tourism-travel/index.vue';
|
||||
import diyVipcardCard from '@/vipcard/components/diy/vipcard-card/index.vue';
|
||||
import diyVipcardReserve from '@/vipcard/components/diy/vipcard-reserve/index.vue';
|
||||
import diyVipcardStore from '@/vipcard/components/diy/vipcard-store/index.vue';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import { onMounted, nextTick, computed, ref,watch } from 'vue';
|
||||
import Sortable from 'sortablejs';
|
||||
|
||||
108
uni-app/src/app/components/diy/notice/index.vue
Normal file
108
uni-app/src/app/components/diy/notice/index.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<view :style="warpCss" class="overflow-hidden">
|
||||
<view class="flex items-center" @click="noticeClickFn">
|
||||
<image v-if="diyComponent.iconType == 'system'" :src="img(`addon/shop/notice/${diyComponent.systemIcon}.png`)" class="h-[44rpx] max-w-[130rpx] -mr-[8rpx]" mode="heightFix"></image>
|
||||
<image v-else :src="img(diyComponent.imageUrl || '')" class="w-[30rpx] h-[30rpx] -mr-[8rpx]" mode="aspectFit"></image>
|
||||
<u-notice-bar :text="diyComponent.list.text" :color="diyComponent.textColor" :bgColor="diyComponent.componentBgColor" :fontSize="diyComponent.fontSize * 2 + 'rpx'" speed="50" icon="" :style="{'fontWeight': diyComponent.fontWeight}"></u-notice-bar>
|
||||
<text class="iconfont iconxiangyoujiantou -ml-[8rpx]" :style="{'color': diyComponent.textColor,'fontSize': (diyComponent.fontSize * 2 + 'rpx'), 'fontWeight': diyComponent.fontWeight}"></text>
|
||||
</view>
|
||||
<u-popup :show="noticeShow" @close="noticeShow = false" :closeable="true" mode="center" :round="5">
|
||||
<view class="py-[30rpx] text-sm leading-none border-solid border-b-[2rpx]">
|
||||
<text class="ml-[30rpx]">公告内容</text>
|
||||
</view>
|
||||
<scroll-view scroll-y="true" class="px-6 py-3 w-[600rpx] h-[500rpx] text-sm font-bold">
|
||||
{{diyComponent.list.text}}
|
||||
</scroll-view>
|
||||
<u-button type="primary" @click="noticeShow = false" shape="circle" class="mx-[30rpx] mb-[40rpx] !w-auto">我知道了</u-button>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 应用列表
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { img, redirect } from '@/utils/common';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
|
||||
const props = defineProps(['component', 'index', 'pullDownRefresh']);
|
||||
const diyStore = useDiyStore();
|
||||
const noticeShow = ref(false);
|
||||
|
||||
const diyComponent = computed(() => {
|
||||
if (diyStore.mode == 'decorate') {
|
||||
return diyStore.value[props.index];
|
||||
} else {
|
||||
return props.component;
|
||||
}
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
if (diyComponent.value.componentBgColor) style += 'background-color:' + diyComponent.value.componentBgColor + ';';
|
||||
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
|
||||
return style;
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.pullDownRefresh,
|
||||
(newValue, oldValue) => {
|
||||
// 处理下拉刷新业务
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
// 装修模式下刷新
|
||||
if (diyStore.mode == 'decorate') {
|
||||
watch(
|
||||
() => diyComponent.value,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue && newValue.componentName == 'AddonList') {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
const refresh = () => {
|
||||
// 装修模式下设置默认图
|
||||
if (diyStore.mode == 'decorate') {
|
||||
if(!diyComponent.value.list.title) diyComponent.value.list.title = '公告名称';
|
||||
}
|
||||
}
|
||||
|
||||
const noticeClickFn = ()=>{
|
||||
if(diyComponent.value.showType == 'popup'){
|
||||
noticeShow.value = true;
|
||||
}else{
|
||||
redirect({ url: diyComponent.value.list.link.url});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 单行超出隐藏 */
|
||||
.using-hidden {
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
/* 多行超出隐藏 */
|
||||
.multi-hidden {
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
</style>
|
||||
5
uni-app/src/app/locale/zh-Hans/pages.member.address.json
Normal file
5
uni-app/src/app/locale/zh-Hans/pages.member.address.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"address": "快递地址",
|
||||
"locationAddress": "同城配送地址",
|
||||
"createAddress": "新建收货地址"
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "收货人",
|
||||
"namePlaceholder": "请输入收货人姓名",
|
||||
"mobile": "手机号码",
|
||||
"mobilePlaceholder": "请输入手机号码",
|
||||
"selectArea":"选择地区",
|
||||
"selectAreaPlaceholder":"请选择地区",
|
||||
"address": "详细地址",
|
||||
"addressPlaceholder": "请填写详细地址",
|
||||
"defaultAddress": "设为默认地址"
|
||||
}
|
||||
92
uni-app/src/app/pages/member/address.vue
Normal file
92
uni-app/src/app/pages/member/address.vue
Normal file
@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<scroll-view scroll-y="true" v-if="!loading">
|
||||
<view class="border-0 !border-b !border-[#eee] border-solid">
|
||||
<u-tabs :list="tabs" @click="switchTab" :current="current" itemStyle="width:50%;height:88rpx;box-sizing: border-box;"></u-tabs>
|
||||
</view>
|
||||
<view class="p-[30rpx]" v-show="current == 0">
|
||||
<view v-for="item in addressList" class="border-0 !border-b !border-[#f5f5f5] border-solid pb-[30rpx] flex items-center">
|
||||
<view class="flex-1">
|
||||
<view class="text-xs text-gray-subtitle">{{ item.full_address.replace(item.address, '') }}</view>
|
||||
<view class="font-bold my-[10rpx]">{{ item.address }}</view>
|
||||
<view class="text-sm">{{ item.name }} <text class="text-[26rpx] text-gray-subtitle">{{ mobileHide(item.mobile) }}</text></view>
|
||||
</view>
|
||||
<text class="iconfont iconbianji" @click="editAddress(item.id)"></text>
|
||||
</view>
|
||||
<view v-if="!addressList.length" class="pt-[20vh]">
|
||||
<u-empty mode="address" :icon="img('/static/resource/images/empty.png')"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="p-[30rpx]" v-show="current == 1">
|
||||
<view v-for="item in locationAddressList" class="border-0 !border-b !border-[#f5f5f5] border-solid pb-[30rpx] flex items-center">
|
||||
<view class="flex-1">
|
||||
<view class="text-xs text-gray-subtitle">{{ item.full_address.replace(item.address, '') }}</view>
|
||||
<view class="font-bold my-[10rpx]">{{ item.address }}</view>
|
||||
<view class="text-sm">{{ item.name }} <text class="text-[26rpx] text-gray-subtitle">{{ mobileHide(item.mobile) }}</text></view>
|
||||
</view>
|
||||
<text class="iconfont iconbianji" @click="editAddress(item.id)"></text>
|
||||
</view>
|
||||
<view v-if="!locationAddressList.length" class="pt-[15vh]">
|
||||
<u-empty mode="address" :icon="img('/static/resource/images/empty.png')"/>
|
||||
</view>
|
||||
</view>
|
||||
<u-tabbar :fixed="true" :safeAreaInsetBottom="true" :border="false">
|
||||
<view class="p-[24rpx] pt-0 w-full">
|
||||
<u-button type="primary" shape="circle" :text="t('createAddress')" @click="addAddress"></u-button>
|
||||
</view>
|
||||
</u-tabbar>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { redirect, img } from '@/utils/common'
|
||||
import { getAddressList } from '@/app/api/member'
|
||||
import { t } from '@/locale'
|
||||
|
||||
const loading = ref(true)
|
||||
const current = ref(0)
|
||||
const tabs = ref([
|
||||
{ name: t('address'), key: 'address' },
|
||||
{ name: t('locationAddress'), key: 'location_address' }
|
||||
])
|
||||
const addressList = ref<object[]>([])
|
||||
const locationAddressList = ref<object[]>([])
|
||||
|
||||
getAddressList({})
|
||||
.then(({ data }) => {
|
||||
const address = [], locationAddress = []
|
||||
data.forEach(item => {
|
||||
item.type == 'address' ? address.push(item) : locationAddress.push(item)
|
||||
})
|
||||
addressList.value = address
|
||||
locationAddressList.value = locationAddress
|
||||
loading.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
const switchTab = (event)=> {
|
||||
current.value = event.index
|
||||
}
|
||||
|
||||
const addAddress = ()=> {
|
||||
const url = `/app/pages/member/${tabs.value[ current.value ].key}_edit`
|
||||
redirect({ url })
|
||||
}
|
||||
|
||||
const editAddress = (id: number)=> {
|
||||
const url = `/app/pages/member/${tabs.value[ current.value ].key}_edit`
|
||||
redirect({ url, param: { id } })
|
||||
}
|
||||
|
||||
const mobileHide = (mobile: string) => {
|
||||
return mobile.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.u-tabs__wrapper__nav__line) {
|
||||
bottom: 0;
|
||||
}
|
||||
</style>
|
||||
144
uni-app/src/app/pages/member/address_edit.vue
Normal file
144
uni-app/src/app/pages/member/address_edit.vue
Normal file
@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<view class="px-[30rpx]">
|
||||
<u-form labelPosition="left" :model="formData" labelWidth="200rpx" errorType='toast' :rules="rules"
|
||||
ref="formRef">
|
||||
<view class="mt-[10rpx]">
|
||||
<u-form-item :label="t('name')" prop="name" :border-bottom="true">
|
||||
<u-input v-model="formData.name" border="none" clearable
|
||||
:placeholder="t('namePlaceholder')"></u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="mt-[10rpx]">
|
||||
<u-form-item :label="t('mobile')" prop="mobile" :border-bottom="true">
|
||||
<u-input v-model="formData.mobile" border="none" clearable
|
||||
:placeholder="t('mobilePlaceholder')"></u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="mt-[10rpx]">
|
||||
<u-form-item :label="t('selectArea')" prop="area" :border-bottom="true" @click="selectArea">
|
||||
<view v-if="!formData.area" class="text-gray-placeholder text-[30rpx]">{{ t('selectAreaPlaceholder') }}</view>
|
||||
<view v-else class="text-[30rpx]">{{ formData.area }}</view>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="mt-[10rpx]">
|
||||
<u-form-item :label="t('address')" prop="address" :border-bottom="true">
|
||||
<u-input v-model="formData.address" border="none" clearable
|
||||
:placeholder="t('addressPlaceholder')"></u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="mt-[10rpx]">
|
||||
<u-form-item :label="t('defaultAddress')" prop="name" :border-bottom="true">
|
||||
<u-switch v-model="formData.is_default" size="20" :activeValue="1" :inactiveValue="0"></u-switch>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="mt-[40rpx]">
|
||||
<u-button type="primary" shape="circle" :text="t('save')" @click="save" :loading="operateLoading"></u-button>
|
||||
</view>
|
||||
</u-form>
|
||||
</view>
|
||||
|
||||
<area-select ref="areaRef" @complete="areaSelectComplete" :area-id="formData.district_id"/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { redirect } from '@/utils/common'
|
||||
import { t } from '@/locale'
|
||||
import { addAddress, editAddress, getAddressInfo } from '@/app/api/member'
|
||||
|
||||
const formData = ref({
|
||||
id: 0,
|
||||
name: '',
|
||||
mobile: '',
|
||||
province_id: 0,
|
||||
city_id: 0,
|
||||
district_id: 0,
|
||||
address: '',
|
||||
full_address: '',
|
||||
is_default: 0,
|
||||
area: '',
|
||||
type: 'address'
|
||||
})
|
||||
|
||||
onLoad((data) => {
|
||||
if (data.id) {
|
||||
getAddressInfo(data.id)
|
||||
.then(({ data }) => {
|
||||
data && Object.assign(formData.value, data)
|
||||
})
|
||||
.catch()
|
||||
}
|
||||
})
|
||||
|
||||
const areaRef = ref()
|
||||
const formRef = ref(null)
|
||||
|
||||
const rules = computed(() => {
|
||||
return {
|
||||
'name': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: t('namePlaceholder'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
'mobile': [
|
||||
{
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: t('mobilePlaceholder'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
{
|
||||
validator() {
|
||||
return uni.$u.test.mobile(formData.value.mobile)
|
||||
},
|
||||
message: t('mobileError')
|
||||
}
|
||||
],
|
||||
'area': {
|
||||
validator() {
|
||||
return !uni.$u.test.isEmpty(formData.value.area)
|
||||
},
|
||||
message: t('selectAreaPlaceholder')
|
||||
},
|
||||
'address': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: t('addressPlaceholder'),
|
||||
trigger: ['blur', 'change'],
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const selectArea = () => {
|
||||
areaRef.value.open()
|
||||
}
|
||||
|
||||
const areaSelectComplete = (event) => {
|
||||
formData.value.province_id = event.province.id || 0
|
||||
formData.value.city_id = event.city.id || 0
|
||||
formData.value.district_id = event.district.id || 0
|
||||
formData.value.area = `${event.province.name || ''}${event.city.name || ''}${event.district.name || ''}`
|
||||
}
|
||||
|
||||
const operateLoading = ref(false)
|
||||
const save = ()=> {
|
||||
const save = formData.id ? editAddress : addAddress
|
||||
|
||||
formRef.value.validate().then(() => {
|
||||
if (operateLoading.value) return
|
||||
operateLoading.value = true
|
||||
|
||||
formData.value.full_address = formData.value.area + formData.value.address
|
||||
|
||||
save(formData.value).then((res) => {
|
||||
operateLoading.value = false
|
||||
}).catch(() => {
|
||||
operateLoading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
7
uni-app/src/app/pages/member/location_address_edit.vue
Normal file
7
uni-app/src/app/pages/member/location_address_edit.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<view></view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
158
uni-app/src/components/area-select/area-select.vue
Normal file
158
uni-app/src/components/area-select/area-select.vue
Normal file
@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<u-popup :show="show" @close="show = false" mode="bottom" :round="10" :closeable="true">
|
||||
<view class="text-center p-[30rpx]">请选择地区</view>
|
||||
|
||||
<view class="flex p-[30rpx] text-sm font-semibold">
|
||||
<view v-if="areaList.province.length" class="pr-[50rpx]" :class="{'text-[red]': currSelect == 'province'}" @click="currSelect = 'province'">
|
||||
<view v-if="selected.province">{{ selected.province.name }}</view>
|
||||
<view v-else>请选择</view>
|
||||
</view>
|
||||
<view v-if="areaList.city.length" class="pr-[50rpx]" :class="{'text-[red]': currSelect == 'city' }" @click="currSelect = 'city'">
|
||||
<view v-if="selected.city">{{ selected.city.name }}</view>
|
||||
<view v-else>请选择</view>
|
||||
</view>
|
||||
<view v-if="areaList.district.length" class="pr-[50rpx]" :class="{'text-[red]': currSelect == 'district' }" @click="currSelect = 'district'">
|
||||
<view v-if="selected.district">{{ selected.district.name }}</view>
|
||||
<view v-else>请选择</view>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view scroll-y="true" class="h-[50vh]">
|
||||
<view class="flex p-[30rpx] pt-0 text-sm">
|
||||
<view v-if="areaList.province.length" v-show="currSelect == 'province'">
|
||||
<view v-for="item in areaList.province" class="leading-loose" :class="{'text-[red]': selected.province && selected.province.id == item.id }"
|
||||
@click="selected.province = item" >{{ item.name }}</view>
|
||||
</view>
|
||||
<view v-if="areaList.city.length" v-show="currSelect == 'city'">
|
||||
<view v-for="item in areaList.city" class="leading-loose" :class="{'text-[red]': selected.city && selected.city.id == item.id }"
|
||||
@click="selected.city = item">{{ item.name }}</view>
|
||||
</view>
|
||||
<view v-if="areaList.district.length" v-show="currSelect == 'district'">
|
||||
<view v-for="item in areaList.district" class="leading-loose" :class="{'text-[red]': selected.district && selected.district.id == item.id }"
|
||||
@click="selected.district = item">{{ item.name }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { getAreaListByPid, getAreaByCode } from '@/app/api/system'
|
||||
|
||||
const prop = defineProps({
|
||||
areaId: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
const show = ref(false)
|
||||
const areaList = reactive({
|
||||
province: [],
|
||||
city: [],
|
||||
district: []
|
||||
})
|
||||
const currSelect = ref('province')
|
||||
|
||||
const selected = reactive({
|
||||
province: null,
|
||||
city: null,
|
||||
district: null
|
||||
})
|
||||
|
||||
getAreaListByPid(0)
|
||||
.then(({ data }) => {
|
||||
areaList.province = data
|
||||
})
|
||||
.catch()
|
||||
|
||||
watch(() => prop.areaId, (nval, oval)=> {
|
||||
if (nval && !oval) {
|
||||
getAreaByCode(nval).then(({ data }) => {
|
||||
data.province && (selected.province = data.province)
|
||||
data.city && (selected.city = data.city)
|
||||
data.district && (selected.district = data.district)
|
||||
})
|
||||
.catch()
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 监听省变更
|
||||
*/
|
||||
watch(() => selected.province, ()=> {
|
||||
getAreaListByPid(selected.province.id)
|
||||
.then(({ data }) => {
|
||||
areaList.city = data
|
||||
currSelect.value = 'city'
|
||||
|
||||
if (selected.city) {
|
||||
let isExist = false
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (selected.city.id == data[i].id) {
|
||||
isExist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!isExist) {
|
||||
selected.city = null
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch()
|
||||
}, { deep: true })
|
||||
|
||||
/**
|
||||
* 监听市变更
|
||||
*/
|
||||
watch(() => selected.city, (nval)=> {
|
||||
if (nval) {
|
||||
getAreaListByPid(selected.city.id)
|
||||
.then(({ data }) => {
|
||||
areaList.district = data
|
||||
currSelect.value = 'district'
|
||||
|
||||
if (selected.district) {
|
||||
let isExist = false
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (selected.district.id == data[i].id) {
|
||||
isExist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!isExist) {
|
||||
selected.district = null
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch()
|
||||
} else {
|
||||
areaList.district = []
|
||||
selected.district = null
|
||||
}
|
||||
|
||||
}, { deep: true })
|
||||
|
||||
const emits = defineEmits(['complete'])
|
||||
|
||||
/**
|
||||
* 监听区县变更
|
||||
*/
|
||||
watch(() => selected.district, (nval)=> {
|
||||
if (nval) {
|
||||
currSelect.value = 'district'
|
||||
emits('complete', selected)
|
||||
show.value = false
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
const open = ()=> {
|
||||
show.value = true
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -20,5 +20,48 @@
|
||||
"pages.pay.browser": "支付",
|
||||
"pages.pay.result": "支付结果",
|
||||
"pages.setting.index": "设置",
|
||||
"pages.webview.index": ""
|
||||
"pages.webview.index": "",
|
||||
"cms.pages.list": "资讯中心",
|
||||
"cms.pages.detail": "文章详情",
|
||||
"recharge.pages.recharge": "充值",
|
||||
"recharge.pages.recharge_record": "充值记录",
|
||||
"recharge.pages.recharge_record_detail": "充值记录详情",
|
||||
"vipcard.pages.verify.index": "核销",
|
||||
"vipcard.pages.verify.record": "核销记录",
|
||||
"vipcard.pages.verify.detail": "核销详情",
|
||||
"vipcard.pages.reserve.result": "预约结果",
|
||||
"vipcard.pages.order.payment": "订单结算",
|
||||
"vipcard.pages.order.list": "订单列表",
|
||||
"vipcard.pages.order.my_reserved": "我的预约",
|
||||
"vipcard.pages.order.my_reserved_detail": "我的预约详情",
|
||||
"vipcard.pages.order.my_card": "我的卡项",
|
||||
"vipcard.pages.order.detail": "订单详情",
|
||||
"vipcard.pages.service.list": "项目列表",
|
||||
"vipcard.pages.card.list": "卡项列表",
|
||||
"vipcard.pages.card.detail": "卡项详情",
|
||||
"tourism.pages.way.list": "线路列表",
|
||||
"tourism.pages.way.detail": "线路详情",
|
||||
"tourism.pages.way.order": "线路订单",
|
||||
"tourism.pages.hotel.list": "酒店列表",
|
||||
"tourism.pages.hotel.detail": "酒店详情",
|
||||
"tourism.pages.hotel.order": "酒店订单",
|
||||
"tourism.pages.scenic.list": "景点列表",
|
||||
"tourism.pages.scenic.detail": "景点详情",
|
||||
"tourism.pages.scenic.order": "景点订单",
|
||||
"tourism.pages.order.list": "旅游订单",
|
||||
"tourism.pages.order.detail": "订单详情",
|
||||
"tourism.pages.verify.index": "核销",
|
||||
"tourism.pages.verify.record": "核销记录",
|
||||
"tourism.pages.verify.detail": "核销详情",
|
||||
"cms.pages.list": "资讯中心",
|
||||
"cms.pages.detail": "文章详情",
|
||||
"vipcard.pages.service.list": "项目列表",
|
||||
"vipcard.pages.card.list": "卡项列表",
|
||||
"vipcard.pages.card.detail": "卡项详情",
|
||||
"recharge.pages.recharge": "充值",
|
||||
"recharge.pages.recharge_record": "充值记录",
|
||||
"recharge.pages.recharge_record_detail": "充值记录详情",
|
||||
"vipcard.pages.order.my_reserved": "我的预约",
|
||||
"vipcard.pages.order.my_reserved_detail": "我的预约详情",
|
||||
"vipcard.pages.order.my_card": "我的卡项"
|
||||
}
|
||||
@ -73,5 +73,12 @@
|
||||
"waitingOrder": "待使用",
|
||||
"remainOrder": "已完成",
|
||||
"allOrder": "全部订单",
|
||||
"myOrder": "我的订单"
|
||||
"myOrder": "我的订单",
|
||||
|
||||
"orderNo": "订单号",
|
||||
"actualPayment": "实付款",
|
||||
"orderClose": "关闭订单",
|
||||
"orderFinish": "订单完成",
|
||||
"pay": "支付",
|
||||
"orderDetail": "详情"
|
||||
}
|
||||
@ -118,6 +118,27 @@
|
||||
},
|
||||
"needLogin": true
|
||||
},
|
||||
{
|
||||
"path": "app/pages/member/address",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%pages.member.address%"
|
||||
},
|
||||
"needLogin": true
|
||||
},
|
||||
{
|
||||
"path": "app/pages/member/address_edit",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%pages.member.address_edit%"
|
||||
},
|
||||
"needLogin": true
|
||||
},
|
||||
{
|
||||
"path": "app/pages/member/location_address_edit",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%pages.member.address_edit%"
|
||||
},
|
||||
"needLogin": true
|
||||
},
|
||||
{
|
||||
"path": "app/pages/pay/browser",
|
||||
"style": {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3952239 */
|
||||
src: url('//at.alicdn.com/t/c/font_3952239_8vg0d3zzqa7.woff2?t=1694674505913') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_8vg0d3zzqa7.woff?t=1694674505913') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_8vg0d3zzqa7.ttf?t=1694674505913') format('truetype');
|
||||
src: url('//at.alicdn.com/t/c/font_3952239_lebf98cnvzb.woff2?t=1696920277771') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_lebf98cnvzb.woff?t=1696920277771') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_lebf98cnvzb.ttf?t=1696920277771') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@ -13,6 +13,14 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.iconbianji:before {
|
||||
content: "\e6ee";
|
||||
}
|
||||
|
||||
.iconjiahao2fill:before {
|
||||
content: "\e728";
|
||||
}
|
||||
|
||||
.iconkaidian:before {
|
||||
content: "\e6fb";
|
||||
}
|
||||
|
||||
@ -50,7 +50,6 @@ class Request {
|
||||
public get(url: string, data: AnyObject = {}, config: RequestConfig = {}) {
|
||||
config.showSuccessMessage == undefined && (config.showSuccessMessage = false)
|
||||
Object.assign(this.config, config)
|
||||
console.log('get request', url, data)
|
||||
return this.request('GET', url, data)
|
||||
}
|
||||
|
||||
|
||||
74
uni-app/windi.config.ts
Normal file
74
uni-app/windi.config.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { defineConfig } from 'windicss/helpers'
|
||||
|
||||
export default defineConfig({
|
||||
prefixer: false,
|
||||
preflight: false,
|
||||
extract: {
|
||||
exclude: ['node_modules', '.git', 'dist']
|
||||
},
|
||||
theme: {
|
||||
extend: {
|
||||
fontSize: {
|
||||
xs: ['0.75rem', '1rem'], // 12px
|
||||
sm: ['0.875rem', '1.25rem'], // 14px
|
||||
base: ['1rem', '1.5rem'], // 16px
|
||||
lg: ['1.125rem', '1.75rem'], // 18px
|
||||
xl: ['1.66rem', '1.75rem'], // 20px
|
||||
'2xl': ['1.75rem', '2.25rem'] // 28px
|
||||
},
|
||||
colors: {
|
||||
primary: {
|
||||
DEFAULT: 'var(--primary-color)'
|
||||
},
|
||||
red: {
|
||||
DEFAULT: '#ED1B26',
|
||||
dark: '#94171D'
|
||||
},
|
||||
blue: '#276EF1',
|
||||
brown: '#99644C',
|
||||
green: {
|
||||
DEFAULT: '#219653',
|
||||
dark: '#21531C'
|
||||
},
|
||||
orange: '#FB6939',
|
||||
purple: '#7356BF',
|
||||
yellow: '#EEAB27',
|
||||
black: {
|
||||
DEFAULT: '#161616',
|
||||
light: '#363636',
|
||||
pure: '#000000'
|
||||
},
|
||||
gray: {
|
||||
DEFAULT: '#222222',
|
||||
pressed: '#2b2b2b',
|
||||
subtitle: '#757575',
|
||||
description: '#AFAFAF',
|
||||
skeleton: '#2c2c2c',
|
||||
indicator: '#353535',
|
||||
placeholder: '#c0c4cc'
|
||||
},
|
||||
white: '#FFFFFF',
|
||||
background: '#282828',
|
||||
border: 'rgba(117, 117, 117, 0.1)',
|
||||
page: 'var(--page-bg-color)'
|
||||
},
|
||||
spacing: {
|
||||
half: '50%',
|
||||
'7.5': '1.875rem',
|
||||
'22': '5.375rem'
|
||||
},
|
||||
borderRadius: {
|
||||
none: '0',
|
||||
sm: '0.125rem',
|
||||
DEFAULT: '0.25rem',
|
||||
md: '0.375rem',
|
||||
lg: '0.5rem',
|
||||
full: '9999px',
|
||||
large: '12px'
|
||||
}
|
||||
}
|
||||
},
|
||||
corePlugins: {
|
||||
container: false
|
||||
}
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user