feat: 优化弹窗样式

master
绝弹 2023-10-14 21:00:37 +08:00
parent 2f127bac40
commit 5791478337
22 changed files with 158 additions and 419 deletions

View File

@ -1,6 +1,6 @@
<template>
<div>
<div class="bg-white px-4 py-2">
<div class="h-full grid grid-rows-[auto_1fr]">
<div class="bg-white dark:bg-gray-800 px-4 py-2">
<div class="flex justify-between gap-4">
<BreadCrumb></BreadCrumb>
<div>
@ -9,7 +9,7 @@
</div>
</div>
<slot name="content">
<div class="m-4 p-4 bg-white">
<div class="m-4 p-4 bg-white dark:bg-gray-700">
<slot></slot>
</div>
</slot>

View File

@ -191,7 +191,7 @@ export const Table = defineComponent({
<BaseTable
row-key="id"
bordered={true}
bordered={false}
{...this.tableProps}
loading={this.loading}
pagination={this.pagination}

View File

@ -7,7 +7,7 @@ import relativeTime from "dayjs/plugin/relativeTime";
*
*
*/
const DATETIME = "YYYY-MM-DD HH:mm:ss";
const DATETIME = "YYYY-MM-DD HH:mm";
/**
*
@ -63,5 +63,5 @@ dayjs.prototype.format = function (format?: string) {
return this._format(dayjs.DATETIME);
};
export { dayjs, DATETIME, DATE, TIME };
export { DATE, DATETIME, TIME, dayjs };

View File

@ -4,7 +4,7 @@ declare module 'dayjs' {
/**
*
*/
export var DATETIME: 'YYYY-MM-DD HH:mm:ss';
export var DATETIME: 'YYYY-MM-DD HH:mm';
export var DATE: 'YYYY-MM-DD';

View File

@ -29,10 +29,11 @@ export default defineComponent({
renderItem(routes: MenuItem[], isTop = false) {
return routes.map((route) => {
const icon = route.icon ? () => <i class={route.icon} /> : null;
const node = route.children?.length ? (
<a-menu-item-group key={route.path} v-slots={{ icon, title: () => route.title }}>
const node: any = route.children?.length ? (
<>
<div class="px-2"><a-divider margin={6}></a-divider></div>
{this.renderItem(route?.children)}
</a-menu-item-group>
</>
) : (
<a-menu-item key={route.path} v-slots={{ icon }} onClick={() => this.goto(route)}>
{route.title}

View File

@ -4,9 +4,9 @@
class="h-13 overflow-hidden flex justify-between items-center gap-4 px-2 pr-4 border-b border-slate-200 bg-white dark:bg-slate-800 dark:border-slate-700"
>
<div class="h-13 flex items-center border-b border-slate-200 dark:border-slate-800">
<router-link to="/" class="px-2 py-1 rounded flex items-center gap-2 text-slate-700 hover:bg-slate-100">
<router-link to="/" class="px-2 py-2 rounded flex items-center gap-2 text-slate-700">
<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">
<h1 class="relative text-lg leading-[19px] dark:text-white m-0 p-0">
{{ appStore.title }}
<span
v-if="isDev"
@ -41,11 +41,15 @@
:collapsible="true"
:collapsed="isCollapsed"
:hide-trigger="false"
:breakpoint="'lg'"
@collapse="onCollapse"
>
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-hidden pt-2">
<Menu />
</a-scrollbar>
<template #trigger="{ collapsed }">
<i :class="`text-gray-400 text-base ${collapsed ? 'icon-park-outline-expand-left' : 'icon-park-outline-expand-right'}`"></i>
</template>
</a-layout-sider>
<a-layout class="layout-content flex-1">
<a-layout-header class="h-8 bg-white border-b border-slate-200 dark:bg-slate-800 dark:border-slate-700">

View File

@ -64,7 +64,7 @@ import { reactive } from "vue";
const meridiem = dayjs.localeData().meridiem(dayjs().hour(), dayjs().minute());
const appStore = useAppStore();
const userStore = useUserStore();
const model = reactive({ username: "juetan", password: "juetan" });
const model = reactive({ username: "", password: "" });
const route = useRoute();
const router = useRouter();
const loading = ref(false);

30
src/pages/dev/openapi.vue Normal file
View File

@ -0,0 +1,30 @@
<template>
<bread-page>
<template #content>
<iframe
src="https://apifox.com/apidoc/shared-f1ea65e6-cee8-4fe3-949f-288a7cd1af49"
frameborder="0"
class="w-full h-full"
></iframe>
</template>
</bread-page>
</template>
<script setup lang="ts"></script>
<style lang="less" scoped></style>
<route lang="json">
{
"meta": {
"sort": 20010,
"title": "接口文档",
"icon": "icon-park-outline-api"
},
"parentMeta": {
"sort": 20010,
"title": "开发相关",
"icon": "icon-park-outline-home"
}
}
</route>

View File

@ -63,7 +63,7 @@ for (const item of items) {
const state = reactive({
menus: items,
visible: true
visible: false
});
const indeter = (items: any[]) => {
@ -200,11 +200,6 @@ const table = useTable({
"sort": 10201,
"title": "表格组件",
"icon": "icon-park-outline-add-subtract"
},
"parentMeta": {
"sort": 10201,
"title": "内置组件",
"icon": "icon-park-outline-add-subtract"
}
}
</route>

26
src/pages/home/home.vue Normal file
View File

@ -0,0 +1,26 @@
<template>
<div class="m-4">
<a-card :bordered="false">
<template #title>
<div class="flex items-center">
<i class="icon-park-outline-config mr-2"></i>
系统参数
</div>
</template>
</a-card>
</div>
</template>
<script setup lang="tsx"></script>
<style scoped></style>
<route lang="json">
{
"meta": {
"sort": 10001,
"title": "首页",
"icon": "icon-park-outline-home"
}
}
</route>

View File

@ -1,164 +0,0 @@
<template>
<div class="m-4 p-4 bg-white dark:bg-gray-700">
<Form v-bind="form"></Form>
</div>
</template>
<script setup lang="tsx">
import { Form, useForm } from "@/components";
const sleep = (wait: number) => new Promise((res) => setTimeout(res, wait));
const form = useForm({
items: [
{
field: "username",
label: "姓名",
type: "input",
required: true,
itemProps: {
hideLabel: false,
},
rules: ["password"],
},
{
field: "nickname",
label: "昵称",
type: "input",
disable: ({ model }) => !model.username,
rules: [
{
message: "昵称不能超过 10 个字符",
required: true,
disable: ({ model }) => !model.username,
},
],
},
{
field: "password",
label: "密码",
type: "password",
visible: ({ model }) => model.username,
nodeProps: {
class: "w-full",
},
},
{
field: "gender",
label: "性别",
type: "select",
options: [
{
label: "男",
value: 1,
},
{
label: "女",
value: 2,
},
],
},
{
field: "startTime:endTime",
label: "时间",
type: "time",
nodeProps: {
type: "time-range",
},
help: "时间段",
},
{
field: "startDate:endDate",
label: "日期",
type: "dateRange",
},
{
field: "checkbox",
label: "多选",
type: "checkbox",
options: [
{
label: "选项1",
value: 1,
},
{
label: "选项2",
value: 2,
},
],
},
{
field: "radio",
label: "单选",
type: "radio",
options: [
{
label: "选项1",
value: 1,
},
{
label: "选项2",
value: 2,
},
],
},
{
field: "slider",
label: "音量",
type: "slider",
},
{
field: "provice:city:town",
label: "城市",
type: "cascader",
nodeProps: {
checkStrictly: true,
pathMode: true,
},
options: [
{
label: "广西",
value: "gx",
children: [
{
label: "南宁",
value: "nn",
},
{
label: "桂林",
value: "gl",
children: [
{
label: "阳朔",
value: "ys",
},
{
label: "临桂",
value: "lg",
},
],
},
],
},
],
},
],
submit: async ({ model }) => {
await sleep(3000);
console.log("submit", model);
return { message: "操作成功" };
},
});
</script>
<style scoped></style>
<route lang="json">
{
"meta": {
"sort": 10001,
"title": "首页111",
"icon": "icon-park-outline-home"
}
}
</route>

View File

@ -1,159 +0,0 @@
<template>
<div class="m-4 p-4 bg-white">
<Table v-bind="table"></Table>
</div>
</template>
<script setup lang="tsx">
import { ContentType, api } from "@/api";
import { Table, useTable } from "@/components";
import { dayjs } from "@/libs/dayjs";
import { Avatar } from "@arco-design/web-vue";
const url = ref<any>(null);
const table = useTable({
data: async () => {
return [];
},
columns: [
{
type: "index",
},
{
title: "姓名",
dataIndex: "username",
width: 200,
render: ({ record }) => {
return (
<div class="flex items-center gap-2 w-full">
<div>
<Avatar size={32}>
<img src={record.avatar} width={32} height={32} />
</Avatar>
</div>
<div class="flex-1 overflow-hidden">
<span class="ml-0">{record.nickname}</span>
<div class="text-xs text-gray-400 mt-1 truncate">{record.description}</div>
</div>
</div>
);
},
},
{
title: "昵称",
dataIndex: "username",
},
{
title: "昵称",
dataIndex: "username",
width: 200,
render: ({ record }) => {
return (
<div class="">
<span class="ml-0">{record.username}</span>
<div class="text-xs text-gray-400 mt-1 truncate">创建于 {dayjs(record.createAt).format()}</div>
</div>
);
},
},
{
title: "操作",
type: "button",
width: 70,
buttons: [],
},
],
search: {
items: [
{
field: "username",
label: "姓名",
type: "input",
},
],
},
create: {
title: "新建用户",
modalProps: {
width: 432,
maskClosable: false,
},
formProps: {
layout: "vertical",
},
model: {
avatar: "11",
},
items: [
{
field: "username",
label: "姓名",
type: "input",
required: true,
},
{
field: "nickname",
label: "昵称",
type: "input",
},
{
field: "password",
label: "密码",
type: "password",
},
{
label: "头像",
field: "avatar",
type: "input",
component: ({ model, field }) => {
const onInputChange = (e: Event) => {
const target = e.target as HTMLInputElement;
const file = target.files?.[0];
if (!file) {
return;
}
model[field] = file;
console.log(file);
const reader = new FileReader();
reader.onload = (e) => {
url.value = e.target?.result;
};
reader.readAsDataURL(file);
};
return (
<div class="w-full h-12 flex gap-4 items-center justify-between">
<input type="file" onChange={onInputChange} class="flex-1" />
{url.value && (
<a-avatar size={40}>
<img src={url.value} />
</a-avatar>
)}
</div>
);
},
},
],
submit: ({ model }) => {
return api.user.addUser(model as any, {
type: ContentType.FormData,
});
},
},
modify: {
title: "修改用户",
},
});
</script>
<style scoped></style>
<route lang="json">
{
"meta": {
"sort": 10001,
"title": "首页",
"icon": "icon-park-outline-home"
}
}
</route>

View File

@ -2,11 +2,6 @@
<bread-page id="list-page">
<template #default>
<div class="flex justify-between items-end gap-4">
<div class="">
<span class="text-lg font-bold text-gray-900">媒体素材</span>
<div class="mt-1 text-gray-400">用户上传的图片视频音频等素材可用于文章图文视频等内容的编辑</div>
</div>
<div class="text-sm text-gray-400">
<a-button type="primary" @click="visible = true">
<template #icon>
<i class="icon-park-outline-add"></i>
@ -14,8 +9,7 @@
添加
</a-button>
</div>
</div>
<AList class="mt-4 bg-white" :bordered="true">
<AList class="mt-2 bg-white" :bordered="true">
<template #header>
<div class="flex gap-2 items-center justify-between text-sm bg-[#fbfbfc] px-5 py-2">
<div class="flex gap-4 my-1.5">
@ -159,7 +153,7 @@
<script setup lang="tsx">
import { Modal } from "@arco-design/web-vue";
const visible = ref(false)
const visible = ref(false);
const onRowActionsSelect = () => {
Modal.open({

View File

@ -8,9 +8,8 @@
import { api } from "@/api";
import { RequestOption } from "@arco-design/web-vue";
import axios from "axios";
import { reactive } from "vue";
const modal = reactive({
const modal = ref({
visible: false,
});
@ -50,7 +49,7 @@ const upload = (option: RequestOption) => {
defineExpose({
open: () => {
modal.visible = true;
modal.value.visible = true;
},
});
</script>

View File

@ -32,12 +32,13 @@
<div>
<Table v-bind="table">
<template #action>
<a-button type="primary">
<a-button type="primary" @click="uploadRef?.open()">
<template #icon>
<i class="icon-park-outline-upload"></i>
</template>
上传
</a-button>
<ani-upload ref="uploadRef"></ani-upload>
</template>
</Table>
</div>
@ -50,6 +51,9 @@ import { api } from "@/api";
import { Table, useTable } from "@/components";
import { dayjs } from "@/libs/dayjs";
import numeral from "numeral";
import AniUpload from './components/upload.vue';
const uploadRef = ref<InstanceType<typeof AniUpload>>()
const getIcon = (mimetype: string) => {
if (mimetype.startsWith("image")) {

View File

@ -68,14 +68,28 @@ const table = useTable({
},
],
search: {
button: true,
items: [
{
field: "[startDate, endDate]",
label: "登陆账号",
type: "dateRange",
required: false,
nodeProps: {
showTime: true,
timePickerProps: { defaultValue: ["23:59:59", "00:00:00"] },
},
itemProps: {
hideLabel: true,
},
},
{
field: "nickname",
label: "登陆账号",
type: "search",
type: "input",
required: false,
nodeProps: {
// placeholder: "",
placeholder: "请输入登陆账号",
},
itemProps: {
hideLabel: true,

View File

@ -22,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>
);
},
@ -97,11 +97,6 @@ const table = useTable({
label: "角色标识",
type: "input",
},
{
field: "description",
label: "个人描述",
type: "textarea",
},
{
field: "permissionIds",
label: "关联权限",
@ -111,6 +106,11 @@ const table = useTable({
multiple: true,
},
},
{
field: "description",
label: "个人描述",
type: "textarea",
},
],
submit: ({ model }) => {
return api.role.addRole(model);

View File

@ -1,15 +1,6 @@
<template>
<BreadPage>
<Table v-bind="table">
<template #action>
<a-button status="danger" :disabled="true">
<template #icon>
<i class="icon-park-outline-delete"></i>
</template>
删除
</a-button>
</template>
</Table>
<Table v-bind="table"> </Table>
</BreadPage>
</template>
@ -18,26 +9,19 @@ import { api } from "@/api";
import { Table, useTable } from "@/components";
import { dayjs } from "@/libs/dayjs";
const type = ref("all");
const table = useTable({
data: async (model, paging) => {
return api.user.getUsers({ ...model, ...paging });
},
tableProps: {
rowSelection: {
showCheckedAll: true
}
},
columns: [
{
title: "用户昵称",
dataIndex: "username",
width: 240,
width: 180,
render: ({ record }) => (
<div class="flex items-center">
<a-avatar size={40}>
<img src={record.avatar || "https://github.com/juetan.png"} alt="" />
<a-avatar size={32}>
<img src={`https://picsum.photos/200?${Math.random()}`} alt="" />
</a-avatar>
<span class="ml-2 flex-1 flex flex-col overflow-hidden">
<span>{record.nickname}</span>
@ -84,15 +68,18 @@ const table = useTable({
},
],
search: {
button: false,
items: [
{
extend: "nickname",
required: false,
type: 'search',
enableLoad: true,
itemProps: {
hideLabel: true,
},
nodeProps: {
placeholder: "输入用户昵称关键字",
placeholder: "用户昵称",
},
},
],
@ -100,7 +87,7 @@ const table = useTable({
create: {
title: "新建用户",
modalProps: {
width: 772,
width: 732,
maskClosable: false,
},
formProps: {
@ -119,14 +106,6 @@ const table = useTable({
label: "用户昵称",
type: "input",
},
{
field: "description",
label: "个人描述",
type: "textarea",
itemProps: {
class: "col-span-2",
},
},
{
field: "password",
label: "密码",
@ -142,16 +121,15 @@ const table = useTable({
},
},
{
label: "头像",
field: "avatarId",
type: "custom",
component: ({ field, model }) => {
return (
<a-avatar size={40}>
<img src={model?.[field]} alt="" />
</a-avatar>
);
field: "description",
label: "个人描述",
type: "textarea",
itemProps: {
class: "col-span-2",
},
nodeProps: {
class: 'h-[96px]'
}
},
],
submit: ({ model }) => {

View File

@ -10,7 +10,7 @@ export const router = createRouter({
routes: [
{
path: "/",
redirect: "/home",
redirect: "/home/home",
},
...routes,
],

View File

@ -3,9 +3,10 @@
@arcoblue-6: #08f;
body {
--border-radius-small: 4px;
// --border-radius-small: 4px;
li.arco-dropdown-option {
line-height: 28px;
line-height: 32px;
width: calc(100% - 8px);
margin-left: 4px;
margin-right: 4px;
@ -14,6 +15,21 @@ body {
margin-top: 4px;
}
}
.arco-table-td {
color: rgb(var(--gray-8));
}
.arco-modal {
border-radius: var(--border-radius-small);
overflow: hidden;
}
.arco-modal-header {
background: var(--color-fill-2);
border-bottom: none;
}
.arco-modal-footer {
padding-top: 0;
border-top: none;
}
.arco-layout-sider {
box-shadow: none;
}
@ -52,10 +68,10 @@ body {
background-color: var(--color-neutral-2);
}
&.arco-menu-selected {
// color: @arcoblue-6;
// background-color: @arcoblue-1;
color: #fff;
background-color: @arcoblue-6;
color: @arcoblue-6;
background-color: @arcoblue-1;
// color: #fff;
// background-color: @arcoblue-6;
.arco-menu-icon {
color: inherit;
}

View File

@ -42,9 +42,10 @@ declare module 'vue-router/auto/routes' {
'/_layout/': RouteRecordInfo<'/_layout/', '/_layout', Record<never, never>, Record<never, never>>,
'/_login/': RouteRecordInfo<'/_login/', '/_login', Record<never, never>, Record<never, never>>,
'/[..._all]/': RouteRecordInfo<'/[..._all]/', '/:_all(.*)', { _all: ParamValue<true> }, { _all: ParamValue<false> }>,
'/demo/': RouteRecordInfo<'/demo/', '/demo', Record<never, never>, Record<never, never>>,
'/demo/test': RouteRecordInfo<'/demo/test', '/demo/test', Record<never, never>, Record<never, never>>,
'/home/': RouteRecordInfo<'/home/', '/home', Record<never, never>, Record<never, never>>,
'/dev/openapi': RouteRecordInfo<'/dev/openapi', '/dev/openapi', Record<never, never>, Record<never, never>>,
'/home/demo': RouteRecordInfo<'/home/demo', '/home/demo', Record<never, never>, Record<never, never>>,
'/home/home': RouteRecordInfo<'/home/home', '/home/home', Record<never, never>, Record<never, never>>,
'/home/test': RouteRecordInfo<'/home/test', '/home/test', Record<never, never>, Record<never, never>>,
'/my/': RouteRecordInfo<'/my/', '/my', Record<never, never>, Record<never, never>>,
'/post/': RouteRecordInfo<'/post/', '/post', Record<never, never>, Record<never, never>>,
'/post/category/': RouteRecordInfo<'/post/category/', '/post/category', Record<never, never>, Record<never, never>>,

View File

@ -7,7 +7,7 @@ const delOptions = {
title: "提示",
titleAlign: "start",
width: 432,
content: "危险操作确定删除该数据吗?",
content: "危险操作确定删除该数据吗?",
maskClosable: false,
closable: false,
okText: "确定",