Conflicts:
	src/views/member/user/index.vue
This commit is contained in:
YunaiV 2023-08-26 01:24:30 +08:00
commit 415309469c
14 changed files with 307 additions and 180 deletions

View File

@ -0,0 +1,40 @@
import request from '@/config/axios'
export interface AddressVO {
id: number
name: string
mobile: string
areaId: number
detailAddress: string
defaultStatus: boolean
}
// 查询用户收件地址列表
export const getAddressPage = async (params) => {
return await request.get({ url: `/member/address/page`, params })
}
// 查询用户收件地址详情
export const getAddress = async (id: number) => {
return await request.get({ url: `/member/address/get?id=` + id })
}
// 新增用户收件地址
export const createAddress = async (data: AddressVO) => {
return await request.post({ url: `/member/address/create`, data })
}
// 修改用户收件地址
export const updateAddress = async (data: AddressVO) => {
return await request.put({ url: `/member/address/update`, data })
}
// 删除用户收件地址
export const deleteAddress = async (id: number) => {
return await request.delete({ url: `/member/address/delete?id=` + id })
}
// 导出用户收件地址 Excel
export const exportAddress = async (params) => {
return await request.download({ url: `/member/address/export-excel`, params })
}

View File

@ -1,21 +1,21 @@
import request from '@/config/axios'
export interface UserVO {
areaId: number | undefined
areaName: string | undefined
avatar: string | undefined
birthday: number | undefined
createTime: number | undefined
id: number
mobile: string
password: string
status: number
registerIp: string
loginDate: number | undefined
loginIp: string
loginDate: Date
nickname: string
avatar: string
name: string
sex: number
areaId: number
birthday: Date
mark: string
createTime: Date
mobile: string
name: string | undefined
nickname: string | undefined
registerIp: string
sex: number
status: number
}
// TODO @梦:和 UserVO 搞成一个把。
@ -32,6 +32,7 @@ export interface UserBaseInfoVO {
name: string | null | undefined
sex: number
areaId: number | null | undefined
areaName: string | null | undefined
birthday: Date | null | undefined
mark: string | null | undefined
createTime: Date | null | undefined

View File

@ -434,7 +434,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
meta: { hidden: true },
children: [
{
path: 'user/detail',
path: 'user/detail/:userId',
name: 'MemberUserDetail',
meta: {
title: '会员详情',

View File

@ -11,7 +11,7 @@ import dayjs from 'dayjs'
* @description format + + "YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
* @returns
*/
export function formatDate(date: Date, format?: string): string {
export function formatDate(date: Date | number, format?: string): string {
// 日期不存在,则返回空
if (!date) {
return ''

View File

@ -0,0 +1,80 @@
<template>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="收件地址编号" align="center" prop="id" width="150px" />
<el-table-column label="收件人名称" align="center" prop="name" width="150px" />
<el-table-column label="手机号" align="center" prop="mobile" width="150px" />
<el-table-column label="地区编码" align="center" prop="areaId" width="150px" />
<el-table-column label="收件详细地址" align="center" prop="detailAddress" />
<el-table-column label="是否默认" align="center" prop="defaultStatus" width="150px">
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="Number(scope.row.defaultStatus)" />
</template>
</el-table-column>
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</template>
<script lang="ts" setup>
import { DICT_TYPE } from '@/utils/dict'
defineComponent({
name: 'AddressList'
})
import { defineComponent } from 'vue'
import { dateFormatter } from '@/utils/formatTime'
import * as AddressApi from '@/api/member/address'
const { userId }: { userId: number } = defineProps({
userId: {
type: Number,
required: true
}
})
const loading = ref(true) //
const total = ref(0) //
const list = ref([]) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
userId: NaN,
name: null,
mobile: null,
areaId: null,
detailAddress: null,
defaultStatus: null,
createTime: []
})
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await AddressApi.getAddressPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 初始化 **/
onMounted(() => {
queryParams.userId = userId
getList()
})
</script>
<style scoped lang="scss"></style>

View File

@ -17,7 +17,7 @@
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.MEMBER_POINT_BIZ_TYPE)"
:key="dict.value"
:key="dict.value as number"
:label="dict.label"
:value="dict.value"
/>
@ -112,7 +112,7 @@ const queryParams = reactive({
bizType: undefined,
title: null,
createDate: [],
userId: null
userId: NaN
})
const queryFormRef = ref() //
@ -140,9 +140,8 @@ const resetQuery = () => {
handleQuery()
}
// TODO @: userId
const { memberId } = defineProps({
memberId: {
const { userId } = defineProps({
userId: {
type: Number,
required: true
}
@ -150,7 +149,7 @@ const { memberId } = defineProps({
/** 初始化 **/
onMounted(() => {
queryParams.userId = memberId
queryParams.userId = userId
getList()
})
</script>

View File

@ -92,6 +92,7 @@ const list = ref([]) // 列表的数据
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
userId: NaN,
nickname: null,
day: null,
createTime: []
@ -122,9 +123,8 @@ const resetQuery = () => {
handleQuery()
}
// TODO @: userId
const { memberId } = defineProps({
memberId: {
const { userId } = defineProps({
userId: {
type: Number,
required: true
}
@ -132,7 +132,7 @@ const { memberId } = defineProps({
/** 初始化 **/
onMounted(() => {
queryParams.userId = memberId
queryParams.userId = userId
getList()
})
</script>

View File

@ -0,0 +1,90 @@
<template>
<el-card shadow="never">
<template #header>
<slot name="header"></slot>
</template>
<el-row>
<el-col :span="4">
<ElAvatar shape="square" :size="140" :src="user.avatar || undefined" />
</el-col>
<el-col :span="20">
<el-descriptions :column="2">
<el-descriptions-item>
<template #label>
<descriptions-item-label label="用户名" icon="ep:user" />
</template>
{{ user.name || '空' }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<descriptions-item-label label="昵称" icon="ep:user" />
</template>
{{ user.nickname }}
</el-descriptions-item>
<el-descriptions-item label="手机号">
<template #label>
<descriptions-item-label label="手机号" icon="ep:phone" />
</template>
{{ user.mobile }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<descriptions-item-label label="性别" icon="fa:mars-double" />
</template>
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="user.sex" />
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<descriptions-item-label label="所在地" icon="ep:location" />
</template>
{{ user.areaName }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<descriptions-item-label label="注册 IP" icon="ep:position" />
</template>
{{ user.registerIp }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<descriptions-item-label label="生日" icon="fa:birthday-cake" />
</template>
{{ user.birthday ? formatDate(user.birthday) : '空' }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<descriptions-item-label label="注册时间" icon="ep:calendar" />
</template>
{{ user.createTime ? formatDate(user.createTime) : '空' }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<descriptions-item-label label="最后登录时间" icon="ep:calendar" />
</template>
{{ user.loginDate ? formatDate(user.loginDate) : '空' }}
</el-descriptions-item>
</el-descriptions>
</el-col>
</el-row>
</el-card>
</template>
<script setup lang="ts">
import { DICT_TYPE } from '@/utils/dict'
import { formatDate } from '@/utils/formatTime'
import * as UserApi from '@/api/member/user'
import DescriptionsItemLabel from '@/views/member/user/components/descriptions/DescriptionsItemLabel.vue'
const { user }: { user: UserApi.UserVO } = defineProps({
user: {
type: UserApi.UserVO,
required: true
}
})
</script>
<style scoped lang="scss">
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>

View File

@ -1,14 +0,0 @@
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'AddressList'
})
</script>
<!-- TODO @可以读 address -->
<template>
<div>收货地址列表</div>
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,28 @@
<script setup lang="ts">
const { label } = defineProps({
label: {
type: String,
required: true
},
icon: {
type: String,
required: false
}
})
</script>
<template>
<div class="cell-item">
<Icon :icon="icon" />
{{ label }}
</div>
</template>
<style scoped lang="scss">
.cell-item {
display: inline;
}
.cell-item::after {
content: ':';
}
</style>

View File

@ -1,12 +1,11 @@
<template>
<div v-loading="loading">
<el-row :gutter="10" class="detail-info-warp">
<el-row :gutter="10">
<!-- 左上角基本信息 -->
<el-col :span="14" class="detail-info-item">
<el-card shadow="never">
<UserBasicInfo :user="user">
<template #header>
<div class="card-header">
<!-- TODO @如果不要小蓝线是不是直接用 el-card 自带的 title 即可 -->
<CardTitle title="基本信息" />
<el-button
v-if="user.id"
@ -19,100 +18,8 @@
</el-button>
</div>
</template>
<el-row>
<el-col :span="4">
<ElAvatar shape="square" :size="140" :src="user.avatar || undefined" />
</el-col>
<el-col :span="20">
<el-descriptions :column="2">
<el-descriptions-item>
<template #label>
<div class="cell-item">
<Icon icon="ep:user" />
用户名
</div>
</template>
{{ user.name || '空' }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
<Icon icon="ep:user" />
昵称
</div>
</template>
{{ user.nickname }}
</el-descriptions-item>
<el-descriptions-item label="手机号">
<template #label>
<div class="cell-item">
<Icon icon="ep:phone" />
手机号
</div>
</template>
{{ user.mobile }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
<Icon icon="fa:mars-double" />
性别
</div>
</template>
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="user.sex" />
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
<Icon icon="ep:location" />
所在地
</div>
</template>
<!-- TODO @这里后端返回的时候要返回 areaName -->
{{ user.areaId }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
<Icon icon="ep:position" />
注册 IP
</div>
</template>
{{ user.registerIp }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
<Icon icon="fa:birthday-cake" />
生日
</div>
</template>
{{ user.birthday ? formatDate(user.birthday) : '空' }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
<Icon icon="ep:calendar" />
注册时间
</div>
</template>
{{ user.createTime ? formatDate(user.createTime) : '空' }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
<Icon icon="ep:calendar" />
最后登录时间
</div>
</template>
{{ user.loginDate ? formatDate(user.loginDate) : '空' }}
</el-descriptions-item>
</el-descriptions>
</el-col>
</el-row>
</el-card>
</UserBasicInfo>
</el-col>
<!-- 右上角账户信息 -->
<el-col :span="10" class="detail-info-item">
<el-card shadow="never">
@ -122,7 +29,6 @@
<AccountInfo />
</el-card>
</el-col>
<!-- 下边账户明细 -->
<!-- TODO 芋艿收货地址订单管理售后管理收藏记录优惠劵 -->
<el-card header="账户明细" style="width: 100%; margin-top: 20px" shadow="never">
@ -131,78 +37,77 @@
</template>
<el-tabs v-model="activeName">
<el-tab-pane label="积分" name="point">
<PointList v-if="user.id" :member-id="user.id" />
<PointList v-if="user.id" :user-id="user.id" />
</el-tab-pane>
<el-tab-pane label="签到" name="sign">
<SignList v-if="user.id" :member-id="user.id" />
<SignList v-if="user.id" :user-id="user.id" />
</el-tab-pane>
<el-tab-pane label="成长值" name="third">成长值(WIP)</el-tab-pane>
<el-tab-pane label="余额" name="fourth">余额(WIP)</el-tab-pane>
<el-tab-pane label="收货地址" name="address">
<AddressList v-if="user.id" :user-id="user.id" />
</el-tab-pane>
<el-tab-pane label="订单管理" name="fourth">订单管理(WIP)</el-tab-pane>
<el-tab-pane label="售后管理" name="fourth">售后管理(WIP)</el-tab-pane>
<el-tab-pane label="收藏记录" name="fourth">收藏记录(WIP)</el-tab-pane>
<el-tab-pane label="优惠劵" name="fourth">优惠劵(WIP)</el-tab-pane>
</el-tabs>
</el-card>
</el-row>
</div>
<!-- 表单弹窗添加/修改 -->
<UserForm ref="formRef" @success="getUserData(user.id)" />
<UserForm ref="formRef" v-if="user.id" @success="getUserData(user.id)" />
</template>
<script setup lang="ts">
// TODO @ vue
import PointList from '@/views/member/user/components/point-list.vue'
import SignList from '@/views/member/user/components/sign-list.vue'
import CardTitle from '@/views/member/user/components/card-title.vue'
// TODO @UserApi
import { getUser, UserBaseInfoVO } from '@/api/member/user'
import { formatDate } from '@/utils/formatTime'
import { DICT_TYPE } from '@/utils/dict'
import PointList from '@/views/member/user/components/PointList.vue'
import SignList from '@/views/member/user/components/SignList.vue'
import CardTitle from '@/views/member/user/components/CardTitle.vue'
import * as UserApi from '@/api/member/user'
import UserForm from '@/views/member/user/UserForm.vue'
// TODO @ AccountInfo
import AccountInfo from '@/views/member/user/components/account-info.vue'
import AccountInfo from '@/views/member/user/components/AccountInfo.vue'
import UserBasicInfo from '@/views/member/user/components/UserBasicInfo.vue'
import AddressList from '@/views/member/user/components/AddressList.vue'
defineOptions({ name: 'MemberDetail' })
const activeName = ref('point') // tabs
const loading = ref(true) //
let user = ref<UserBaseInfoVO>({
let user = ref<UserApi.UserVO>({
areaId: undefined,
avatar: undefined,
birthday: undefined,
createTime: undefined,
id: undefined,
id: NaN,
loginDate: undefined,
loginIp: '',
mark: '',
mobile: '',
name: '',
nickname: '',
password: null,
registerIp: undefined,
registerIp: '',
sex: 0,
status: 0
status: 0,
areaName: ''
})
/** 获得用户 */
const getUserData = async (id: number) => {
loading.value = true
try {
user.value = await getUser(id)
} finally {
loading.value = false
}
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 获得用户 */
const getUserData = async (id: number) => {
loading.value = true
try {
user.value = await UserApi.getUser(id)
} finally {
loading.value = false
}
}
/** 初始化 */
const route = useRoute()
const router = useRouter()
// TODO @ id query
// TODO @
const member_id = route.query.member_id as number
const member_id = Number(route.params.userId as string)
onMounted(() => {
if (!member_id) {
// TODO
@ -227,11 +132,4 @@ onMounted(() => {
justify-content: space-between;
align-items: center;
}
.cell-item {
display: inline;
}
/** TODO 下面 css 貌似没啥用? */
.cell-item::after {
content: ':';
}
</style>

View File

@ -111,6 +111,13 @@
/>
<el-table-column label="操作" align="center" width="140px" fixed="right">
<template #default="scope">
<el-button
link
type="primary"
@click="goMemberDetail(scope.row.id)"
>
详情
</el-button>
<el-button
link
type="primary"
@ -148,7 +155,6 @@
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import * as UserApi from '@/api/member/user'
import UserForm from './UserForm.vue'
import { DICT_TYPE } from '@/utils/dict'
import MemberTagSelect from '@/views/member/tag/components/MemberTagSelect.vue'
import MemberLevelSelect from '@/views/member/level/components/MemberLevelSelect.vue'
@ -198,10 +204,9 @@ const resetQuery = () => {
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
const { push } = useRouter()
const goMemberDetail = (id: number) => {
push({ name: 'MemberUserDetail', params: { member_id: id } })
}
/** 初始化 **/