feat: 优化个人设置页面

master
luoer 2023-08-16 20:17:24 +08:00
parent 66ea7f5436
commit b1600f6721
23 changed files with 373 additions and 110 deletions

View File

@ -133,7 +133,7 @@ export const FormModal = defineComponent({
<Button type="primary">
{{
default: () => (typeof props.trigger === "string" ? props.trigger : "添加"),
icon: () => <i class="icon-park-outline-plus" />,
icon: () => <i class="icon-park-outline-add" />,
}}
</Button>
);

View File

@ -6,6 +6,7 @@ import {
Input,
InputNumber,
InputPassword,
InputSearch,
RadioGroup,
RangePicker,
Select,
@ -49,6 +50,16 @@ export const nodeMap = {
allowClear: true,
} as InstanceType<typeof Input>["$props"],
},
/**
*
*/
search: {
component: InputSearch,
nodeProps: {
placeholder: "请输入",
allowClear: true,
} as InstanceType<typeof InputSearch>["$props"],
},
/**
*
*/

View File

@ -1,4 +1,4 @@
import { Button } from "@arco-design/web-vue";
import { Button, Link } from "@arco-design/web-vue";
import { IconRefresh, IconSearch } from "@arco-design/web-vue/es/icon";
export const config = {
@ -11,20 +11,22 @@ export const config = {
searchItemSubmit: {
field: "id",
type: "custom",
label: ' ',
itemProps: {
class: "table-search-item col-start-4 !mr-0 grid grid-cols-[0_1fr]",
hideLabel: true,
// hideLabel: true,
},
component: () => {
const tableRef = inject<any>("ref:table");
return (
<div class="w-full flex gap-x-2 justify-end">
{/* <Link>收起 <i class="icon-park-outline-up"></i> </Link> */}
{(tableRef.search?.items?.length || 0) > config.searchInlineCount && (
<Button disabled={tableRef?.loading.value} onClick={() => tableRef?.reloadData()}>
{{ icon: () => <IconRefresh></IconRefresh>, default: () => "重置" }}
</Button>
)}
<Button type="primary" loading={tableRef?.loading.value} onClick={() => tableRef?.loadData()}>
<Button type="outline" loading={tableRef?.loading.value} onClick={() => tableRef?.loadData()}>
{{ icon: () => <IconSearch></IconSearch>, default: () => "查询" }}
</Button>
</div>
@ -72,6 +74,6 @@ export const config = {
},
getApiErrorMessage(error: any): string {
const message = error?.response?.data?.message || error?.message || "请求失败";
return message;
return '';
},
};

View File

@ -158,14 +158,14 @@ export const Table = defineComponent({
render() {
(this.columns as any).instance = this;
return (
<div class="bh-table w-full">
<div class="table w-full">
{!this.inlined && (
<div class="pb-5 border-b border-slate-200 mb-5">
<Form ref="searchRef" class="grid grid-cols-4 gap-x-4" {...this.search}></Form>
<div class="border-b pb-2 border-slate-200 mb-5">
<Form ref="searchRef" class="!grid grid-cols-4 gap-x-6" {...this.search}></Form>
</div>
)}
<div class={`mb-2 flex justify-between ${!this.inlined && "mt-2"}`}>
<div class={`mb-3 flex justify-between ${!this.inlined && "mt-2"}`}>
<div class="flex-1 flex gap-2">
{this.create && (
<FormModal ref="createRef" onSubmited={this.reloadData} {...(this.create as any)}></FormModal>

View File

@ -167,11 +167,11 @@ export const useTable = (optionsOrFn: UseTableOptions | (() => UseTableOptions))
if (item.extend) {
const createItem = createItems.find((i) => i.field === item.extend);
if (createItem) {
searchItems.push(merge({}, createItem, item));
searchItems.push(merge({ itemProps: { hideLabel: true } }, createItem, item));
continue;
}
}
searchItems.push(item);
searchItems.push(merge({ itemProps: { hideLabel: true } }, item));
}
searchItems.push(config.searchItemSubmit);
options.search.items = searchItems;

View File

@ -28,11 +28,11 @@ export default defineComponent({
renderItem(routes: MenuItem[], isTop = false) {
return routes.map((route) => {
const icon = route.icon && isTop ? () => <i class={route.icon} /> : null;
const icon = route.icon ? () => <i class={route.icon} /> : null;
const node = route.children?.length ? (
<a-sub-menu key={route.path} v-slots={{ icon, title: () => route.title }}>
<a-menu-item-group key={route.path} v-slots={{ icon, title: () => route.title }}>
{this.renderItem(route?.children)}
</a-sub-menu>
</a-menu-item-group>
) : (
<a-menu-item key={route.path} v-slots={{ icon }} onClick={() => this.goto(route)}>
{route.title}
@ -51,6 +51,7 @@ export default defineComponent({
breakpoint="xl"
selectedKeys={this.selectedKeys}
autoOpenSelected={true}
levelIndent={0}
>
{this.renderItem(menus, true)}
</a-menu>

View File

@ -5,9 +5,10 @@
>
<div class="h-13 flex items-center border-b border-slate-200 dark:border-slate-800">
<router-link to="/" class="ml-1 flex items-center gap-2 text-slate-700">
<img src="/favicon.ico" alt="" width="20" height="20" />
<h1 class="text-lg leading-[19px] dark:text-white m-0 p-0">
<img src="/favicon.ico" alt="" width="22" height="22" class="" />
<h1 class="relative text-lg font-semibold leading-[19px] dark:text-white m-0 p-0">
{{ appStore.title }}
<span v-if="isDev" class="absolute -right-14 -top-1 text-xs font-normal text-blue-500 bg-blue-50 px-2 rounded-full"></span>
</h1>
</router-link>
</div>
@ -22,7 +23,7 @@
<a-dropdown>
<span class="cursor-pointer">
<a-avatar :size="28">
<img :src="userStore.avatar" :alt="userStore.nickname" />
<img :src="userStore.avatar || 'https://github.com/juetan.png'" :alt="userStore.nickname" />
</a-avatar>
<span class="mx-2">
{{ userStore.nickname }}
@ -45,7 +46,7 @@
<a-layout class="flex flex-1 overflow-hidden">
<a-layout-sider
class="h-full overflow-hidden dark:bg-slate-800 border-r border-slate-200 dark:border-slate-700"
:width="208"
:width="224"
:collapsed-width="52"
:collapsible="true"
:collapsed="isCollapsed"
@ -86,6 +87,7 @@ const themeConfig = ref({ visible: false });
const onCollapse = (val: boolean) => {
isCollapsed.value = val;
};
const isDev = import.meta.env.DEV
const buttons = [
{
@ -109,7 +111,7 @@ const userButtons = [
icon: "icon-park-outline-config",
text: "个人设置",
onClick: () => {
console.log("个人设置");
router.push('/my')
},
},
{

View File

@ -13,7 +13,7 @@
</div>
<div class="flex items-center justify-center w-full overflow-hidden">
<div
class="login-box w-[960px] h-[560px] relative mx-8 grid md:grid-cols-2 rounded overflow-hidden border border-blue-100"
class="login-box w-[960px] h-[560px] relative mx-4 grid md:grid-cols-2 rounded overflow-hidden border border-blue-100"
>
<div class="relative hidden md:block w-full h-full overflow-hidden bg-[#09f] px-4">
<img src="@/assets/td.svg" :alt="appStore.title" class="w-full h-full select-none" />

View File

@ -1,42 +1,30 @@
<template>
<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>
<a-card title="菜单权限">
<a-tree :data="items" :field-names="{ title: 'title' }" checkable></a-tree>
</a-card>
</bread-page>
</template>
<script setup lang="ts">
<script setup lang="tsx">
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.icon) {
const icon = item.icon;
item.icon = () => <i class={icon}></i>;
}
item.switcherIcon = () => null;
if (item.children) {
for (const child of item.children) {
if (child.icon) {
const icon = child.icon;
child.icon = () => <i class={icon}></i>;
}
child.checked = false;
}
}

View File

@ -1,7 +1,7 @@
<template>
<bread-page id="list-page">
<template #default>
<div class="flex justify-between gap-4">
<div class="flex justify-between items-end gap-4">
<div class="">
<span class="text-base font-semibold text-gray-900">媒体素材</span>
<div class="mt-1 text-gray-400">
@ -9,14 +9,12 @@
</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>
</div>
<a-button type="primary">
<template #icon>
<i class="icon-park-outline-add"></i>
</template>
添加
</a-button>
</div>
</div>
<AList class="mt-4 bg-white" :bordered="true">
@ -41,8 +39,8 @@
</ADoption>
<ADoption v-for="j in 10">
<div class="flex items-center gap-1 w-48">
<AAvatar :size="20" class="mr-1">
<img src="https://picsum.photos/seed/picsum/200/300" alt="" />
<AAvatar :size="20" class="mr-1 bg-slate-50">
<img :src="`https://picsum.photo1s/seed/picsum/200/300?${Math.random()}`" alt="" />
</AAvatar>
绝弹土豆
</div>
@ -56,30 +54,23 @@
</span>
<template #content>
<ADoption>
<template #icon>
<i class="icon-park-outline-check"></i>
</template>
<div class="w-48">默认</div>
</ADoption>
<ADoption>
<template #icon>
<i class="icon-park-outline-sort-amount-up"></i>
</template>
按创建时间升序
</ADoption>
<ADoption>
<template #icon>
<i class="icon-park-outline-sort-amount-down"></i>
</template>
按创建时间降序
</ADoption>
<ADoption>
<template #icon>
<i class="icon-park-outline-align-text-top"></i>
</template>
按文件大小升序
</ADoption>
<ADoption>
<template #icon>
<i class="icon-park-outline-align-text-bottom"></i>
</template>
按文件大小降序
</ADoption>
</template>
@ -108,7 +99,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?12" height="32" width="48">
<AImage :src="`https://picsum.photos/200/300?${Math.random()}`" height="32" width="48" class="bg-slate-50">
</AImage>
</template>
<template #title>
@ -125,7 +116,7 @@
</span>
<span class="text-xs text-gray-400">2023-08-17 17:00:01</span>
<ADropdown @select="onRowActionsSelect" position="br">
<span class="inline-flex p-1 hover:bg-slate-100 rounded cursor-pointer">
<span class="inline-flex p-1 hover:bg-slate-100 text-brand-500 rounded cursor-pointer">
<i class="icon-park-outline-more"></i>
</span>
<template #content>

View File

@ -156,7 +156,7 @@ const form = useForm({
<route lang="json">
{
"meta": {
"sort": 10101,
"sort": 20101,
"title": "首页111",
"icon": "icon-park-outline-home"
}

View File

@ -151,9 +151,14 @@ const table = useTable({
<route lang="json">
{
"meta": {
"sort": 10101,
"sort": 10301,
"title": "首页",
"icon": "icon-park-outline-home"
},
"parentMeta": {
"title": "总览",
"sort": 10000,
"icon": "icon-park-outline-home"
}
}
</route>

BIN
src/pages/my/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

View File

@ -1,14 +1,24 @@
<template>
<BreadPage>
<template #content>
<section class="my-page m-5 bg-white p-4">
<div>
<div class="font-semibold text-base flex items-center">个人设置</div>
<div class="text-sm text-gray-400">此表情符号和消息会显示在您的个人资料和界面中</div>
<section class="my-page m-5 bg-white py-4 px-6 max-w-[980px] mx-auto">
<div class="flex items-end justify-between gap-4 banner">
<div>
<div class="font-semibold text-xl flex items-center">个人设置</div>
<div class="mt-1 text-sm text-gray-400">此表情符号和消息会显示在您的个人资料和界面中</div>
</div>
<div class=" text-gray-500">
<div class="text-right">绝弹 <span class="text-sm font-normal text-gray-500">(juetan)</span></div>
<div class="mt-0.5 text-gray-400 text-xs">创建于 2023年08月18日 </div>
</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="md:w-48 text-gray-400">基本信息</div> -->
<!-- <div class="mb-2 md:w-64">
<div class="text-base font-semibold">基本设置</div>
<div class="text-gray-400 mt-1">设置你的基本信息用于个性化显示</div>
</div> -->
<div class="flex-1 flex">
<a-form :model="user" layout="vertical">
<a-form-item label="个人头像">
@ -18,19 +28,33 @@
<i class="icon-park-outline-edit"></i>
</template>
</a-avatar>
<template #help> 支持 5MB 以内大小, png jpg 格式的图片 </template>
</a-form-item>
<a-form-item label="用户昵称">
<a-input v-model="user.nickname" placeholder="请输入" class="!w-[432px]"></a-input>
<template #help> 用作系统内显示的名称可在后台修改 </template>
</a-form-item>
<a-form-item label="个人描述">
<a-textarea v-model="user.description" placeholder="请输入" class="!w-[580px] h-24"></a-textarea>
<a-textarea v-model="user.description" placeholder="请输入" class="!w-[432px] h-24"></a-textarea>
</a-form-item>
<a-form-item label="密码">
<a-button >修改密码</a-button>
</a-form-item>
<a-form-item label="性别">
<a-radio-group v-model="user.gender" type="button">
<a-radio :value="1"></a-radio>
<a-radio :value="2"></a-radio>
<a-radio :value="3">保密</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="出生日期">
<a-date-picker v-model="user.birth"></a-date-picker>
</a-form-item>
</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="md:flex md:gap-4 mt-2 pt-6 border-t">
<!-- <div class="md:w-48 text-gray-400">联系方式</div> -->
<div class="flex-1">
<a-form :model="user" layout="vertical">
<a-form-item label="消息推送">
@ -52,7 +76,7 @@
</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="md:w-48 text-gray-400">偏好设置</div> -->
<div class="flex-1">
<a-form :model="user" layout="vertical">
<a-form-item label="主题">
@ -73,15 +97,52 @@
</div>
</div>
<div class="md:flex md:gap-4 mt-6 pt-6 border-t">
<div class="md:w-56 text-gray-400">
<!-- <div class="md:w-48 text-gray-400">
其他功能
</div>
</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>
<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>
@ -90,19 +151,15 @@
</div>
<div>
<a-switch checked-color="#3c9">
<template #checked>
已启用
</template>
<template #unchecked>
未启用
</template>
<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="md:w-48 text-gray-400"></div> -->
<div class="flex-1">
<a-button type="primary">更新设置</a-button>
</div>
@ -114,7 +171,7 @@
</template>
<script setup lang="tsx">
import { reactive } from 'vue';
import { reactive } from "vue";
const emailOptions = [
{
@ -127,14 +184,15 @@ const emailOptions = [
},
];
const user =reactive({
const user = reactive({
nickname: "绝弹",
description: "选择在公开个人资料中显示私有项目的贡献,但不显示任何项目,仓库或组织信息",
theme: "dark",
email: "810335188@qq.com",
msg: [2],
})
gender: 1,
birth: '1988-12-18'
});
</script>
<style lang="less">

78
src/pages/post/edit.vue Normal file
View File

@ -0,0 +1,78 @@
<template>
<div>
<div class="bg-white px-4 pt-2">
<bread-crumb></bread-crumb>
<div class="flex justify-between items-end gap-4 bg-white px-1 py-4">
<div>
<div class="text-lg font-semibold">新增文章</div>
<div class="text-gray-400 mt-1">新增的文章需审核才能展现</div>
</div>
<div>
<a-button class="mr-2">保存为草稿</a-button>
<a-button type="primary">保存发布</a-button>
</div>
</div>
</div>
<div class="flex gap-4 mt-6 px-6">
<div class="flex-1 bg-white p-4">
<a-form :model="{}" layout="vertical">
<a-form-item label="标题">
<a-input placeholder="请输入标题" :max-length="120" :show-word-limit="true"></a-input>
</a-form-item>
<a-form-item label="文章内容">
<a-textarea placeholder="说点啥" :max-length="1000" :show-word-limit="true"></a-textarea>
</a-form-item>
</a-form>
</div>
<div class="w-64 bg-white p-4">
<a-form :model="{}" layout="vertical">
<a-form-item label="别名">
<a-input placeholder="请输入"></a-input>
<template #help>
用作URL的别名, 只能包含字母数字下划线和破折号
</template>
</a-form-item>
<a-form-item label="分类">
<a-checkbox-group direction="vertical">
<a-checkbox>开发工具</a-checkbox>
<a-checkbox>日常记录</a-checkbox>
<a-checkbox>心得体验</a-checkbox>
</a-checkbox-group>
</a-form-item>
<a-form-item label="封面图">
<div class="h-24 rounded w-full flex items-center justify-center text-gray-500 bg-gray-100">
从素材库中选择...
</div>
<template #help>
推荐使用 600x400 的图片
</template>
</a-form-item>
</a-form>
</div>
</div>
</div>
</template>
<script setup lang="tsx" name="PostPage">
import { api } from "@/api";
import { Table, useTable } from "@/components";
import { dayjs } from "@/plugins";
</script>
<style lang="less">
.export-form {
.arco-form-item-content {
display: block;
}
}
</style>
<route lang="json">
{
"meta": {
"sort": 10300,
"title": "新增文章",
"icon": "icon-park-outline-edit"
}
}
</route>

View File

@ -1,6 +1,51 @@
<template>
<div class="m-4 p-4 bg-white">
<Table v-bind="table" />
<Table v-bind="table">
<template #action>
<a-button type="outline" @click="model.visible = true">
<template #icon>
<i class="icon-park-outline-export"></i>
</template>
导出
</a-button>
</template>
</Table>
<a-modal title="导出为文件" v-model:visible="model.visible" title-align="start">
<a-form :model="{}" layout="vertical" class="export-form">
<a-form-item label="文件名">
<a-input v-model="model.filename" placeholder="请输入"></a-input>
</a-form-item>
<a-form-item label="导出类型">
<div class="grid gap-2">
<div
v-for="item in exportTypes"
@click="model.exportType = item.name"
class="w-full flex justify-between items-center gap-4 rounded py-2 px-4 border border-transparent cursor-pointer"
:class="{
'!border-brand-500': model.exportType === item.name,
}"
>
<div class="flex items-center gap-2 rounded">
<div class="h-10 w-10 flex items-center justify-center rounded-full bg-brand-50">
<i :class="item.icon" class="text-2xl text-brand-500"></i>
</div>
<div>
<div class="text-slate-900">
{{ item.label }}
</div>
<div class="text-slate-400 text-xs">
{{ item.description }}
</div>
</div>
</div>
<div>
<a-radio v-model="model.exportType" :value="item.name" class="mt-1"></a-radio>
</div>
</div>
</div>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
@ -62,6 +107,22 @@ const table = useTable({
extend: "title",
required: false,
},
{
extend: "title",
required: false,
},
{
extend: "title",
required: false,
},
{
extend: "title",
required: false,
},
{
extend: "title",
required: false,
},
],
},
create: {
@ -108,9 +169,42 @@ const table = useTable({
},
},
});
const exportTypes = [
{
name: "excel",
icon: "icon-park-outline-file-excel",
label: "Excel格式",
description: "后缀: .xlsx, 可使用 office excel 2003 及以上版本打开",
},
{
name: "csv",
icon: "icon-park-outline-file-code",
label: "CSV格式",
description: "后缀: .csv, 可使用 excel 或 记事本等工具打开",
},
{
name: "text",
icon: "icon-park-outline-file-text",
label: "TEXT格式",
description: "后缀: .txt, 可使用 记事本 或 其他文本编辑器打开",
},
];
const model = reactive({
visible: false,
exportType: "excel",
filename: dayjs().format("导出文件YYYYMMDDHHmmss"),
});
</script>
<style scoped></style>
<style lang="less">
.export-form {
.arco-form-item-content {
display: block;
}
}
</style>
<route lang="json">
{
@ -118,6 +212,11 @@ const table = useTable({
"sort": 10300,
"title": "文章管理",
"icon": "icon-park-outline-document-folder"
},
"parentMeta": {
"sort": 10300,
"title": "内容管理",
"icon": "icon-park-outline-document-folder"
}
}
</route>

View File

@ -14,9 +14,6 @@ const table = useTable({
return api.permission.getPermissions();
},
columns: [
{
type: "index",
},
{
title: "权限名称",
dataIndex: "username",
@ -25,7 +22,7 @@ const table = useTable({
return (
<div class="flex flex-col overflow-hidden">
<span>{record.name}</span>
<span class="text-gray-400 text-xs truncate">标识{record.slug}</span>
<span class="text-gray-400 text-xs truncate">{record.slug}</span>
</div>
);
},
@ -43,12 +40,16 @@ const table = useTable({
{
title: "操作",
type: "button",
width: 70,
width: 110,
buttons: [
{
type: "modify",
text: "修改",
},
{
type: 'delete',
text: '删除',
}
],
},
],

View File

@ -14,9 +14,6 @@ const table = useTable({
return api.role.getRoles();
},
columns: [
{
type: "index",
},
{
title: "角色名称",
dataIndex: "username",
@ -25,7 +22,7 @@ const table = useTable({
return (
<div class="flex flex-col overflow-hidden">
<span>{record.name}</span>
<span class="text-gray-400 text-xs truncate">标识{record.slug}</span>
<span class="text-gray-400 text-xs truncate">{record.slug}</span>
</div>
);
},

View File

@ -22,9 +22,6 @@ const table = useTable({
return api.user.getUsers({ ...model, ...paging });
},
columns: [
{
type: "index",
},
{
title: "用户昵称",
dataIndex: "username",
@ -36,7 +33,7 @@ const table = useTable({
</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 class="text-gray-400 text-xs truncate">{record.username}</span>
</span>
</div>
),
@ -82,6 +79,9 @@ const table = useTable({
itemProps: {
hideLabel: true,
},
nodeProps: {
placeholder: "输入用户昵称关键字",
}
},
],
},

View File

@ -65,6 +65,9 @@ body {
display: none;
}
}
.arco-form-item-layout-inline:last-child {
margin-right: 0;
}
}
.dark {
.arco-menu-item.arco-menu-selected {

View File

@ -10,3 +10,8 @@
display: inline-block;
vertical-align: middle;
}
.table .arco-form-item-layout-inline {
margin-right: 8px;
margin-bottom: 0;
}

View File

@ -8,16 +8,18 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
AAvatar: typeof import('@arco-design/web-vue')['Avatar']
ABadge: typeof import('@arco-design/web-vue')['Badge']
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']
ADatePicker: typeof import('@arco-design/web-vue')['DatePicker']
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']
@ -33,9 +35,12 @@ declare module '@vue/runtime-core' {
AListItemMeta: typeof import('@arco-design/web-vue')['ListItemMeta']
AMenu: typeof import('@arco-design/web-vue')['Menu']
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
AMenuItemGroup: typeof import('@arco-design/web-vue')['MenuItemGroup']
AModal: typeof import('@arco-design/web-vue')['Modal']
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']
@ -44,6 +49,7 @@ declare module '@vue/runtime-core' {
ATag: typeof import('@arco-design/web-vue')['Tag']
ATextarea: typeof import('@arco-design/web-vue')['Textarea']
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
ATree: typeof import('@arco-design/web-vue')['Tree']
BreadCrumb: typeof import('./../components/breadcrumb/bread-crumb.vue')['default']
BreadPage: typeof import('./../components/breadcrumb/bread-page.vue')['default']
Page403: typeof import('./../components/error/page-403.vue')['default']

View File

@ -90,6 +90,22 @@ export default defineConfig(({ mode }) => {
* @see https://github.com/unocss/unocss#readme
*/
Unocss({
theme: {
colors: {
brand: {
50: 'rgb(var(--primary-1))',
100: 'rgb(var(--primary-2))',
200: 'rgb(var(--primary-3))',
300: 'rgb(var(--primary-4))',
400: 'rgb(var(--primary-5))',
500: 'rgb(var(--primary-6))',
600: 'rgb(var(--primary-7))',
700: 'rgb(var(--primary-8))',
800: 'rgb(var(--primary-9))',
900: 'rgb(var(--primary-10))',
}
}
},
include: ["src/**/*.{vue,ts,tsx,css,scss,sass,less,styl}"],
presets: [presetUno(), presetIcons({ prefix: "" })],
}),