feat: 优化个人页面

master
绝弹 2023-08-14 21:57:52 +08:00
parent 414ee924b9
commit 66ea7f5436
11 changed files with 292 additions and 39 deletions

2
.env
View File

@ -2,7 +2,7 @@
# 应用配置
# =====================================================================================
# 网站标题
VITE_TITLE = 绝弹管理系统
VITE_TITLE = 绝弹管理后台
# 网站副标题
VITE_SUBTITLE = 快速开发web应用的模板工具
# API接口前缀参见 axios 的 baseURL

View File

@ -1,3 +1,4 @@
import { modal } from "@/utils/modal";
import { Doption, Dropdown, Link, Message, TableColumnData } from "@arco-design/web-vue";
import { isArray, merge } from "lodash-es";
import { reactive } from "vue";
@ -5,7 +6,6 @@ import { useFormModal } from "../form";
import { TableInstance } from "./table";
import { config } from "./table.config";
import { UseTableOptions } from "./use-interface";
import { modal } from "@/utils/modal";
const onClick = async (item: any, columnData: any, getTable: any) => {
if (item.type === "modify") {
@ -128,17 +128,21 @@ export const useTable = (optionsOrFn: UseTableOptions | (() => UseTableOptions))
);
});
const trigger = () => (
<span class="inline-flex p-1 hover:bg-slate-200 rounded cursor-pointer">
<span class="px-2 py-[1px] h-6 vertical-b rounded cursor-pointer text-[rgb(var(--link-6))] hover:bg-[var(--color-fill-2)]">
<i class="icon-park-outline-more"></i>
</span>
);
return (
<Dropdown position="br">
{{
default: trigger,
content: content,
}}
</Dropdown>
<>
<Link></Link>
<Link></Link>
<Dropdown position="br">
{{
default: trigger,
content: content,
}}
</Dropdown>
</>
);
};
}

View File

@ -1,8 +1,68 @@
<template>
<bread-page class="">Demo Page</bread-page>
<bread-page class="">
<div v-for="menu in state.menus" :key="menu.id" class="mt-8">
<div class="flex justify-between pb-1.5 border-b">
<a-checkbox v-model="menu.checked" :indeterminate="indeter(menu.children)">
<span class="font-semibold">
{{ menu.title }}
</span>
</a-checkbox>
<a-link> 设置权限 </a-link>
</div>
<div class="flex gap-4 mt-4">
<template v-if="menu.children">
<a-checkbox
v-model="item.checked"
@change="onItemChange(item, menu)"
v-for="item in menu.children || []"
:key="item.id"
>
{{ item.title }}
</a-checkbox>
</template>
<template v-else>
<div class="text-gray-400">暂无子项</div>
</template>
</div>
</div>
</bread-page>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { menus } from "@/router";
import { cloneDeep } from "lodash-es";
const items = cloneDeep(menus) as any;
for (const item of items) {
item.checked = false;
if (item.children) {
for (const child of item.children) {
child.checked = false;
}
}
}
const state = reactive({
menus: items,
});
const indeter = (items: any[]) => {
if (!items) {
return false;
}
const checked = items.filter((item) => item.checked);
return checked.length > 0 && checked.length < items.length;
};
const onItemChange = (item: any, menu: any) => {
const checked = menu.children.filter((item: any) => item.checked);
if (checked === 0) {
menu.checked = false;
} else if (checked === menu.children.length) {
menu.checked = true;
}
};
</script>
<style scoped></style>

View File

@ -1,20 +1,23 @@
<template>
<bread-page id="list-page">
<template #default>
<div>
<div class="flex justify-between items-center gap-4">
<div class="flex justify-between gap-4">
<div class="">
<span class="text-base font-semibold text-gray-900">媒体素材</span>
<div class="mt-1 text-gray-400">
用户上传的图片视频音频等素材可用于文章图文视频等内容的编辑
</div>
</div>
<div class="text-sm text-gray-400">
<div>
<a-button type="primary">
<template #icon>
<i class="icon-park-outline-plus"></i>
</template>
添加</a-button>
添加
</a-button>
</div>
</div>
<div class="text-sm text-gray-400">
用户上传的图片视频音频等素材可用于文章图文视频等内容的编辑
</div>
</div>
<AList class="mt-4 bg-white" :bordered="true">
<template #header>
@ -105,8 +108,7 @@
<AListItemMeta title="测试图片.png" description="image/png 1.2MB">
<template #avatar>
<ACheckbox class="mr-3"></ACheckbox>
<AImage src="https://picsum.photos/seed/picsum/200/300" height="40">
<img src="https://picsum.photos/seed/picsum/200/300" alt="" />
<AImage src="https://picsum.photos/seed/picsum/200/300?12" height="32" width="48">
</AImage>
</template>
<template #title>

View File

@ -3,14 +3,14 @@
<template #content>
<section class="my-page m-5 bg-white p-4">
<div>
<div class="font-semibold text-lg flex items-center">个人设置</div>
<div class="mt-1 text-sm text-gray-400">此表情符号和消息会显示在您的个人资料和界面中</div>
<div class="font-semibold text-base flex items-center">个人设置</div>
<div class="text-sm text-gray-400">此表情符号和消息会显示在您的个人资料和界面中</div>
</div>
<div>
<div class="md:flex md:gap-4 mt-3 border-t pt-6">
<div class="md:w-56 text-gray-400">基本信息</div>
<div class="flex-1 flex">
<a-form layout="vertical">
<a-form :model="user" layout="vertical">
<a-form-item label="个人头像">
<a-avatar :size="64">
<img src="https://github.com/juetan.png" alt="" />
@ -24,7 +24,7 @@
<template #help> 用作系统内显示的名称可在后台修改 </template>
</a-form-item>
<a-form-item label="个人描述">
<a-textarea v-model="user.description" placeholder="请输入" class="!w-[432px]"></a-textarea>
<a-textarea v-model="user.description" placeholder="请输入" class="!w-[580px] h-24"></a-textarea>
</a-form-item>
</a-form>
</div>
@ -32,7 +32,7 @@
<div class="md:flex md:gap-4 mt-6 pt-6 border-t">
<div class="md:w-56 text-gray-400">联系方式</div>
<div class="flex-1">
<a-form layout="vertical">
<a-form :model="user" layout="vertical">
<a-form-item label="消息推送">
<div>
<a-checkbox-group direction="vertical" v-model="user.msg">
@ -54,7 +54,7 @@
<div class="md:flex md:gap-4 mt-6 pt-6 border-t">
<div class="md:w-56 text-gray-400">偏好设置</div>
<div class="flex-1">
<a-form layout="vertical">
<a-form :model="user" layout="vertical">
<a-form-item label="主题">
<div>
<a-radio-group v-model="user.theme" direction="vertical">
@ -72,6 +72,35 @@
</a-form>
</div>
</div>
<div class="md:flex md:gap-4 mt-6 pt-6 border-t">
<div class="md:w-56 text-gray-400">
其他功能
</div>
<div class="flex-1 grid">
<div class="mb-3">功能列表</div>
<div v-for="i in 3" class="border-t py-4 flex justify-between items-center gap-4">
<div class="flex gap-3 items-center">
<div class="p-2 bg-slate-100 rounded">
<svg t="1692015760052" class="icon h-8 w-10" viewBox="0 0 1264 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7353" width="200" height="200"><path d="M0 333.884235a90.352941 90.352941 0 0 1 90.352941-90.352941h1084.235294a90.352941 90.352941 0 0 1 90.352941 90.352941v271.058824h-256a105.411765 105.411765 0 1 0 0 210.823529H1264.941176v90.352941a90.352941 90.352941 0 0 1-90.352941 90.352942H90.352941a90.352941 90.352941 0 0 1-90.352941-90.352942v-572.235294z m1008.941176 421.647059a45.176471 45.176471 0 1 0 0-90.352941 45.176471 45.176471 0 0 0 0 90.352941z" fill="#C1D0FF" p-id="7354"></path><path d="M90.352941 190.343529a60.235294 60.235294 0 0 1 66.861177-59.843764l963.764706 107.068235A60.235294 60.235294 0 0 1 1174.588235 297.441882v187.030589a60.235294 60.235294 0 0 1-60.235294 60.235294H150.588235a60.235294 60.235294 0 0 1-60.235294-60.235294V190.343529zM140.649412 83.275294A90.352941 90.352941 0 0 1 249.615059 13.251765l886.573176 205.914353L139.264 90.051765l1.385412-6.746353z" fill="#0B77FF" p-id="7355"></path><path d="M0 331.294118a90.352941 90.352941 0 0 1 90.352941-90.352942h1084.235294a90.352941 90.352941 0 0 1 90.352941 90.352942v271.058823h-256a105.411765 105.411765 0 1 0 0 210.82353H1264.941176v90.352941a90.352941 90.352941 0 0 1-90.352941 90.352941H90.352941a90.352941 90.352941 0 0 1-90.352941-90.352941V331.294118z" fill="#E8E8E8" fill-opacity=".1" p-id="7356"></path><path d="M90.352941 271.058824h1084.235294a60.235294 60.235294 0 0 1 60.235294 60.235294v240.941176h-225.882353a135.529412 135.529412 0 1 0 0 271.058824H1234.823529v60.235294a60.235294 60.235294 0 0 1-60.235294 60.235294H90.352941a60.235294 60.235294 0 0 1-60.235294-60.235294V331.294118a60.235294 60.235294 0 0 1 60.235294-60.235294z m1174.588235 60.235294a90.352941 90.352941 0 0 0-90.352941-90.352942H90.352941a90.352941 90.352941 0 0 0-90.352941 90.352942v572.235294a90.352941 90.352941 0 0 0 90.352941 90.352941h1084.235294a90.352941 90.352941 0 0 0 90.352941-90.352941v-90.352941h-256a105.411765 105.411765 0 1 1 0-210.82353H1264.941176v-271.058823z" fill="#FFFFFF" fill-opacity=".6" p-id="7357"></path><path d="M353.882353 424.237176H271.058824l82.823529 195.764706h-41.411765v39.152942h82.82353v39.152941h-82.82353v39.152941h82.82353v78.305882h82.823529v-78.305882h82.823529v-39.152941h-82.823529v-39.152941h82.823529v-39.152942H519.529412l82.823529-195.764706h-82.823529l-82.82353 195.764706-82.823529-195.764706z" fill="#FFFFFF" p-id="7358"></path></svg>
</div>
<div>
<div class="text-gray-900">支付功能</div>
<div class="text-gray-400 mt-1">通知管理员由企业互联的管理员来设置拥有通知业务的最大权限</div>
</div>
</div>
<div>
<a-switch checked-color="#3c9">
<template #checked>
已启用
</template>
<template #unchecked>
未启用
</template>
</a-switch>
</div>
</div>
</div>
</div>
<div class="md:flex md:gap-4 mt-6 pt-6 border-t">
<div class="md:w-56 text-gray-400"></div>
<div class="flex-1">
@ -108,8 +137,8 @@ const user =reactive({
})
</script>
<style lang="less" scoped>
.my-page :deep {
<style lang="less">
.my-page {
.arco-form-item.arco-form-item-error,
.arco-form-item.arco-form-item-has-help {
margin-bottom: 20px;

View File

@ -35,8 +35,7 @@ const table = useTable({
{
title: "操作",
type: "dropdown",
width: 60,
align: "center",
width: 140,
dropdowns: [
{
type: "modify",

View File

@ -146,13 +146,8 @@ const table = useTable({
<route lang="json">
{
"meta": {
"sort": 10301,
"title": "用户管理",
"icon": "icon-park-outline-user"
},
"parentMeta": {
"title": "系统管理",
"icon": "icon-park-outline-setting",
"icon": "icon-park-outline-config",
"sort": 20000
}
}

View File

@ -0,0 +1,164 @@
<template>
<BreadPage>
<template #content>
<a-tabs default-active-key="1" size="large" class="bg-white m-4">
<a-tab-pane key="1" title="全部">
<Table v-bind="table" class="px-4 pb-4"></Table>
</a-tab-pane>
<a-tab-pane key="2" title="已通过(12)"></a-tab-pane>
</a-tabs>
</template>
</BreadPage>
</template>
<script setup lang="tsx">
import { api } from "@/api";
import { Table, useTable } from "@/components";
import { dayjs } from "@/plugins";
import { Avatar, Button } from "@arco-design/web-vue";
const table = useTable({
data: async (model, paging) => {
return api.user.getUsers({ ...model, ...paging });
},
columns: [
{
type: "index",
},
{
title: "用户昵称",
dataIndex: "username",
width: 200,
render: ({ record }) => (
<div class="flex items-center">
<Avatar size={32}>
<img src={record.avatar} alt="" />
</Avatar>
<span class="ml-2 flex-1 flex flex-col overflow-hidden">
<span>{record.nickname}</span>
<span class="text-gray-400 text-xs truncate">账号{record.username}</span>
</span>
</div>
),
},
{
title: "用户描述",
dataIndex: "description",
},
{
title: "用户邮箱",
dataIndex: "email",
},
{
title: "创建时间",
dataIndex: "createdAt",
width: 200,
render: ({ record }) => dayjs(record.createdAt).format(),
},
{
title: "操作",
type: "button",
width: 148,
buttons: [
{
type: "modify",
text: "修改",
},
{
type: "delete",
text: "删除",
onClick: async ({ record }) => {
return api.user.delUser(record.id, { toast: true });
},
},
],
},
],
search: {
items: [
{
extend: "nickname",
required: false,
itemProps: {
hideLabel: true,
},
},
],
},
create: {
title: "新建用户",
trigger: () => (
<Button type="primary">
{{
icon: () => <i class="icon-park-outline-people-plus-one" />,
default: () => "添加",
}}
</Button>
),
modalProps: {
width: 772,
maskClosable: false,
},
formProps: {
layout: "vertical",
class: "!grid grid-cols-2 gap-x-3",
},
items: [
{
field: "username",
label: "登录账号",
type: "input",
required: true,
},
{
field: "nickname",
label: "用户昵称",
type: "input",
},
{
field: "description",
label: "个人描述",
type: "input",
},
{
field: "password",
label: "密码",
type: "password",
},
{
label: "头像",
field: "avatar",
type: "select",
},
{
field: "[startTime,endTime]",
label: "日期范围",
type: "dateRange",
nodeProps: {},
},
],
submit: ({ model }) => {
return api.user.addUser(model);
},
},
modify: {
extend: true,
title: "修改用户",
submit: ({ model }) => {
return api.user.updateUser(model.id, model);
},
},
});
</script>
<style scoped></style>
<route lang="json">
{
"meta": {
"sort": 10301,
"title": "用户管理",
"icon": "icon-park-outline-user"
}
}
</route>

View File

@ -11,20 +11,18 @@ declare module '@vue/runtime-core' {
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
AButton: typeof import('@arco-design/web-vue')['Button']
ACard: typeof import('@arco-design/web-vue')['Card']
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
ACheckboxGroup: typeof import('@arco-design/web-vue')['CheckboxGroup']
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
ADoption: typeof import('@arco-design/web-vue')['Doption']
ADrawer: typeof import('@arco-design/web-vue')['Drawer']
ADropdown: typeof import('@arco-design/web-vue')['Dropdown']
AEmpty: typeof import('@arco-design/web-vue')['Empty']
AForm: typeof import('@arco-design/web-vue')['Form']
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
AImage: typeof import('@arco-design/web-vue')['Image']
AInput: typeof import('@arco-design/web-vue')['Input']
AInputGroup: typeof import('@arco-design/web-vue')['InputGroup']
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
ALayout: typeof import('@arco-design/web-vue')['Layout']
ALayoutContent: typeof import('@arco-design/web-vue')['LayoutContent']
ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader']
@ -38,9 +36,11 @@ declare module '@vue/runtime-core' {
APagination: typeof import('@arco-design/web-vue')['Pagination']
ARadio: typeof import('@arco-design/web-vue')['Radio']
ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup']
ASelect: typeof import('@arco-design/web-vue')['Select']
ASpace: typeof import('@arco-design/web-vue')['Space']
ASubMenu: typeof import('@arco-design/web-vue')['SubMenu']
ASwitch: typeof import('@arco-design/web-vue')['Switch']
ATabPane: typeof import('@arco-design/web-vue')['TabPane']
ATabs: typeof import('@arco-design/web-vue')['Tabs']
ATag: typeof import('@arco-design/web-vue')['Tag']
ATextarea: typeof import('@arco-design/web-vue')['Textarea']
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']