一大波更新
feat:添加api接入;更新依赖版本;完善框架;更新样式;
This commit is contained in:
parent
8f695d09a3
commit
d23f5d5668
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@ -1,3 +0,0 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
28
package.json
28
package.json
@ -9,18 +9,24 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"ant-design-vue": "4.x",
|
||||
"ant-design-vue": "^4.2.6",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.7.9",
|
||||
"dayjs": "^1.11.10",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.0",
|
||||
"vue-router": "4"
|
||||
"js-md5": "^0.8.3",
|
||||
"pinia": "^2.3.0",
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.5",
|
||||
"@vitejs/plugin-vue": "^4.1.0",
|
||||
"sass": "^1.69.5",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.3.2",
|
||||
"vue-tsc": "^1.4.2"
|
||||
}
|
||||
"@types/node": "^22.10.2",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"sass": "^1.83.0 ",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.0.3",
|
||||
"vue-tsc": "^2.1.10"
|
||||
},
|
||||
"packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
|
||||
}
|
||||
|
8
postcss.config.js
Normal file
8
postcss.config.js
Normal file
@ -0,0 +1,8 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ConfigProvider } from 'ant-design-vue'
|
||||
import PageLoading from "./components/page-loading/index.vue";
|
||||
import { useUserStore } from "./service/user-store.ts";
|
||||
import Login from "./components/login/index.vue";
|
||||
import PageLoading from "@/components/page-loading/index.vue";
|
||||
import { useUserStore } from "@/service/user-store.ts";
|
||||
import Login from "@/components/login/index.vue";
|
||||
|
||||
|
||||
// 登录相关
|
||||
|
29
src/assets/libs.scss
Normal file
29
src/assets/libs.scss
Normal file
@ -0,0 +1,29 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@mixin media-breakpoint-down($name) {
|
||||
@if $name ==sm {
|
||||
@media (max-width: 767px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@if $name ==md {
|
||||
@media (max-width: 991px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@if $name ==lg {
|
||||
@media (max-width: 1199px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@if $name ==xl {
|
||||
@media (max-width: 1399px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
@ -44,7 +44,7 @@ const onInputPaste = (e: ClipboardEvent, pIndex: number, fIndex: number) => {
|
||||
<Button type="primary" @click="saveProductValues(productValues)">保存数据</Button>
|
||||
</div>
|
||||
<div class="calculator">
|
||||
<Input :rows="4" placeholder="请输入计算公式"/>
|
||||
<Input.TextArea :rows="4" placeholder="请输入计算公式"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,11 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import {useUserStore} from "../../service/user-store.ts";
|
||||
import {BizError} from "../../core/errors.ts";
|
||||
import {ref} from "vue";
|
||||
import {AppConfig} from "../../app-config.ts";
|
||||
|
||||
import {useUserStore} from "@/service/user-store.ts";
|
||||
import {AppConfig} from "@/app-config.ts";
|
||||
import { BizError } from "@/types/core.ts";
|
||||
|
||||
import leftImage from './login_pic.png'
|
||||
import {Button} from "../button";
|
||||
import {LoginModel} from "../../service/api/user";
|
||||
|
||||
const loading = ref(false)
|
||||
const message = ref('')
|
||||
@ -25,7 +26,10 @@ const handleSubmit = () => {
|
||||
loading.value = true
|
||||
store
|
||||
.login(params.value)
|
||||
.catch((e: BizError) => message.value = e.message)
|
||||
.catch((e: BizError) => {
|
||||
console.log('login error',e)
|
||||
message.value = e.message
|
||||
})
|
||||
.finally(() => loading.value = false)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
export class BizError extends Error {
|
||||
code: number;
|
||||
|
||||
constructor(code: number, message: string='') {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
17
src/core/role.ts
Normal file
17
src/core/role.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export enum RoleEnum {
|
||||
// super user
|
||||
ROOT = 'root',
|
||||
// admin user
|
||||
ADMIN = 'admin',
|
||||
// common user
|
||||
USER = 'user',
|
||||
}
|
||||
export const RoleList = [
|
||||
{ label: '超级管理员', value: RoleEnum.ROOT },
|
||||
{ label: '管理员', value: RoleEnum.ADMIN },
|
||||
]
|
||||
export const AllRoleList = [
|
||||
|
||||
{ label: '全部', value: '' },
|
||||
...RoleList
|
||||
]
|
5
src/core/string.ts
Normal file
5
src/core/string.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { md5 } from "js-md5";
|
||||
|
||||
export function getMd5(str: string) {
|
||||
return md5(str);
|
||||
}
|
@ -41,7 +41,7 @@ const handleMenuClick = ({ key }: MenuInfo) => {
|
||||
<div class="menu-link" v-for="r in currentMenus">
|
||||
<router-link class="menu-item" :to="(r.path || '/')">
|
||||
<div class="menu-icon" :class="r.meta?.icon"></div>
|
||||
<div>{{ r.meta?.title }}</div>
|
||||
<div class="menu-text">{{ r.meta?.title }}</div>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
@ -57,8 +57,8 @@ const handleMenuClick = ({ key }: MenuInfo) => {
|
||||
<MenuItem key="logout">退出登录</MenuItem>
|
||||
</Menu>
|
||||
</template>
|
||||
<Button>
|
||||
{{ store.userInfo?.nickname }}
|
||||
<Button class="flex item-center">
|
||||
<span>{{ store.userInfo?.nickname }}</span>
|
||||
<DownOutlined />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
@ -138,6 +138,7 @@ const handleMenuClick = ({ key }: MenuInfo) => {
|
||||
}
|
||||
|
||||
.app-main-container {
|
||||
min-width: 1100px;
|
||||
padding: 30px;
|
||||
overflow: auto;
|
||||
background: #f0f2f0;
|
||||
|
@ -3,7 +3,7 @@ import { ref, h } from "vue";
|
||||
import { SearchOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import {
|
||||
Pagination, Select, SelectOption, Input, Button,
|
||||
Space, Table,
|
||||
Space,
|
||||
} from 'ant-design-vue'
|
||||
import PageHeader from '../components/page-header.vue'
|
||||
import { fields, getProductValues } from "../service/data";
|
||||
@ -54,12 +54,12 @@ const opts = [
|
||||
<thead>
|
||||
<tr>
|
||||
<th>营养制剂</th>
|
||||
<th v-for="th in columns">{{th.name}}</th>
|
||||
<th>操作</th>
|
||||
<th v-for="th in columns">{{ th.name }}</th>
|
||||
<th width="150">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(tr,rowIndex) in allDataList" :key="rowIndex">
|
||||
<tr v-for="(tr, rowIndex) in allDataList" :key="rowIndex">
|
||||
<td>{{ tr.product.name }}</td>
|
||||
<td v-for="it in tr.values">{{ it.value }}</td>
|
||||
<td>
|
||||
@ -79,37 +79,4 @@ const opts = [
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.search-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin: 10px 0;
|
||||
|
||||
.form-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.search-form {
|
||||
|
||||
.ant-input,
|
||||
.ant-select .ant-select-selector {
|
||||
background-color: #f7f8fa;
|
||||
border: solid 1px transparent;
|
||||
min-width: 100px;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
border-color: var(--primary-color-1-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-result-table {}
|
||||
.data-page{
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss"></style>
|
||||
|
@ -1,8 +1,10 @@
|
||||
<template>
|
||||
<p>输出计算</p>
|
||||
<!-- <DataField /> -->
|
||||
<Button @click="showMessage">test</Button>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import DataField from "@/components/data-fields/index.vue"
|
||||
import Button from "../components/button/button.vue";
|
||||
import {message} from "../components/message";
|
||||
|
||||
|
@ -1,115 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, h } from "vue";
|
||||
import { SearchOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import {
|
||||
Pagination, Select, SelectOption, Input, Button,
|
||||
Space, Table,
|
||||
} from 'ant-design-vue'
|
||||
import { fields, getProductValues } from "../service/data";
|
||||
import PageHeader from '../components/page-header.vue'
|
||||
|
||||
const searchParams = ref({
|
||||
type: '',
|
||||
name: ''
|
||||
})
|
||||
const current = ref(2);
|
||||
const pageSize = ref<number>(20);
|
||||
const columns = fields;
|
||||
const allDataList = getProductValues();
|
||||
console.log(allDataList)
|
||||
|
||||
const opts = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '能量密度', value: 'power' },
|
||||
{ label: 'Pro(g)', value: 'pro' },
|
||||
{ label: 'Na(g)', value: 'na' },
|
||||
]
|
||||
</script>
|
||||
<template>
|
||||
<div class="data-fields-container">
|
||||
<PageHeader title="登录账号管理" description="* 超级管理员: 拥有最高权限,可使用全部管理功能。管理员:普通管理权限,不可新建、删除、编辑账号。" />
|
||||
<div class="search-form">
|
||||
<Space :size="20">
|
||||
<Space class="form-item">
|
||||
<span>营养制剂</span>
|
||||
<Input class="search-item" placeholder="请输入营养制剂" v-model:value="searchParams.name" />
|
||||
</Space>
|
||||
<Space class="form-item">
|
||||
<span>类型</span>
|
||||
<Select class="search-item" v-model:value="searchParams.type">
|
||||
<SelectOption v-for="(it, opIndex) in opts" :key="opIndex" :value="it.value">{{ it.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
</Space>
|
||||
<Space class="form-item" :size="15">
|
||||
<Button :icon="h(SearchOutlined)" type="primary">查询</Button>
|
||||
<Button :icon="h(PlusOutlined)" class="btn-info">新增</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="search-result-table">
|
||||
<div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>营养制剂</th>
|
||||
<th v-for="th in columns">{{ th.name }}</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(tr, rowIndex) in allDataList" :key="rowIndex">
|
||||
<td>{{ tr.product.name }}</td>
|
||||
<td v-for="it in tr.values">{{ it.value }}</td>
|
||||
<td>
|
||||
<Space :size="20">
|
||||
<a>编辑</a>
|
||||
<a>删除</a>
|
||||
</Space>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="data-page">
|
||||
<Pagination v-model:current="current" v-model:page-size="pageSize" :total="50"
|
||||
:show-total="total => `共 ${total} 条`" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.search-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin: 10px 0;
|
||||
|
||||
.form-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.search-form {
|
||||
|
||||
.ant-input,
|
||||
.ant-select .ant-select-selector {
|
||||
background-color: #f7f8fa;
|
||||
border: solid 1px transparent;
|
||||
min-width: 100px;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
border-color: var(--primary-color-1-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-result-table {}
|
||||
|
||||
.data-page {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
132
src/pages/user/index.vue
Normal file
132
src/pages/user/index.vue
Normal file
@ -0,0 +1,132 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, h } from "vue";
|
||||
import { SearchOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import {
|
||||
Pagination, Select, SelectOption, Input, Button, Space, Empty, Spin,
|
||||
Modal,message
|
||||
} from 'ant-design-vue'
|
||||
import { RoleEnum, AllRoleList } from '@/core/role'
|
||||
import { columns, getList,deleteUser } from "@/service/api/user";
|
||||
import PageHeader from '@/components/page-header.vue'
|
||||
import useRequest from "@/service/useRequest";
|
||||
import EditModal from './modal.vue'
|
||||
|
||||
const editUser = ref<UserInfo>()
|
||||
const searchParams = ref<UserSearchParam>({
|
||||
page: 1,
|
||||
limit: 10, role: ''
|
||||
})
|
||||
|
||||
|
||||
const { data: allDataList, loading, run } = useRequest(() => getList(searchParams.value))
|
||||
|
||||
// onMounted(() => {
|
||||
// getList(searchParams.value).then(ret=>{
|
||||
// allDataList.value = ret
|
||||
// })
|
||||
// })
|
||||
function search() {
|
||||
searchParams.value = {
|
||||
...searchParams.value,
|
||||
page: 1,
|
||||
limit: 10
|
||||
}
|
||||
run()
|
||||
}
|
||||
function deleteUserById(id: number) {
|
||||
deleteUser(id).then(() => {
|
||||
message.success('删除成功')
|
||||
run()
|
||||
})
|
||||
}
|
||||
|
||||
function handleDelete(id: number) {
|
||||
Modal.confirm({
|
||||
title:'删除账号',
|
||||
content:'确定要删除该账号吗?',
|
||||
cancelText:'取消',
|
||||
okText:'删除',
|
||||
okType: 'danger',
|
||||
okButtonProps:{
|
||||
|
||||
},
|
||||
onOk:()=>{
|
||||
deleteUserById(id)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="data-fields-container">
|
||||
<PageHeader title="登录账号管理" description="* 超级管理员: 拥有最高权限,可使用全部管理功能。管理员:普通管理权限,不可新建、删除、编辑账号。" />
|
||||
<div class="search-form">
|
||||
<Space :size="20">
|
||||
<Space class="form-item">
|
||||
<span>姓名</span>
|
||||
<Input class="search-item" placeholder="请输入姓名" v-model:value="searchParams.nickname" />
|
||||
</Space>
|
||||
<Space class="form-item">
|
||||
<span>账号</span>
|
||||
<Input class="search-item" placeholder="请输入账号" v-model:value="searchParams.account" />
|
||||
</Space>
|
||||
<Space class="form-item">
|
||||
<span>类型</span>
|
||||
<Select class="search-item" v-model:value="searchParams.role" style="width: 200px;">
|
||||
<SelectOption v-for="(it, opIndex) in AllRoleList" :key="opIndex" :value="it.value">{{ it.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
</Space>
|
||||
<Space class="form-item" :size="15">
|
||||
<Button :icon="h(SearchOutlined)" class="flex item-center" type="primary" @click="search()">查询</Button>
|
||||
<Button :icon="h(PlusOutlined)" class="btn-info flex item-center" @click="editUser = {id:0}">新增</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="search-result-table">
|
||||
<div>
|
||||
<Spin :spinning="loading">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="th in columns">{{ th.title }}</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(user, rowIndex) in allDataList?.list" :key="rowIndex">
|
||||
<td>{{ user.nickname }}</td>
|
||||
<td>{{ user.account }}</td>
|
||||
<td>{{ user.role == RoleEnum.ROOT ? '超级管理员' : '管理员' }}</td>
|
||||
<td>
|
||||
<Space :size="20">
|
||||
<a @click="editUser = user">编辑</a>
|
||||
<a @click="handleDelete(user.id)">删除</a>
|
||||
</Space>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-if="!allDataList || allDataList?.total == 0" style="padding:30px;">
|
||||
<Empty description="暂无数据" />
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
|
||||
<div v-if="allDataList && allDataList?.total > 0" class="data-page">
|
||||
<Pagination v-model:current="searchParams.page" v-model:page-size="searchParams.limit"
|
||||
:total="allDataList.total" :show-total="(total: number) => `共 ${total} 条`" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<EditModal v-if="!!editUser" v-model="editUser" />
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.ant-btn-dangerous{
|
||||
background-color: hsl(359, 100%, 60%);
|
||||
color: white !important;
|
||||
&:hover{
|
||||
background-color: hsl(359, 100%, 70%);
|
||||
}
|
||||
}
|
||||
</style>
|
58
src/pages/user/modal.vue
Normal file
58
src/pages/user/modal.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<script lang="ts" setup>
|
||||
import { defineModel } from 'vue';
|
||||
import { Modal, Form, FormItem, Input, Select, SelectOption, Space, Button } from 'ant-design-vue';
|
||||
import { RoleList } from '@/core/role';
|
||||
|
||||
const editUser = defineModel<UserInfo>();
|
||||
|
||||
function handleSave(values: UserInfo) {
|
||||
console.log(values)
|
||||
}
|
||||
function handleCancel() {
|
||||
editUser.value = undefined;
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Modal :width="400" :open="true" :footer="null" @cancel="handleCancel">
|
||||
<div class="pt-12">
|
||||
<Form @finish="handleSave" autocomplete="off" v-model="editUser" :label-col="{ span: 5 }"
|
||||
:wrapper-col="{ span: 18 }">
|
||||
<FormItem label="用户名" name="nickname" :rules="[{ required: true, message: '请输入姓名' }]">
|
||||
<Input placeholder="请输入姓名" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem label="账号" name="account" :rules="[{ required: true, message: '请输入手机号或邮箱' }]">
|
||||
<Input placeholder="请输入手机号或邮箱" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem label="密码" name="password" :rules="[{
|
||||
validator: async (_:any, value:string) => {
|
||||
console.log('mima',value)
|
||||
if(editUser.value?.id){
|
||||
return true;
|
||||
}
|
||||
if(!value || value.length < 6 || value.length > 12){
|
||||
throw new Error('密码长度6-12位');
|
||||
};
|
||||
return true;
|
||||
}
|
||||
}]">
|
||||
<Input placeholder="请输入6-12位密码" type="password" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem label="角色" name="role" :rules="[{ required: true, message: '请选择用户角色' }]">
|
||||
<Select placeholder="请选择" style="width: 100%;">
|
||||
<SelectOption v-for="(it, opIndex) in RoleList" :key="opIndex" :value="it.value">{{ it.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem class="flex justify-end mt-10 mb-0">
|
||||
<Space :size="20">
|
||||
<Button @click="handleCancel">取消</Button>
|
||||
<Button html-type="submit" class="btn-info">确定</Button>
|
||||
</Space>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
@ -43,7 +43,7 @@ export const routes:RouteRecordRaw[] = [
|
||||
{
|
||||
path: 'user',
|
||||
name: 'user',
|
||||
component: () => import('./pages/user.vue'),
|
||||
component: () => import('./pages/user/index.vue'),
|
||||
meta: {
|
||||
role: 'root',
|
||||
title: '用户管理',
|
||||
|
53
src/service/api/request.ts
Normal file
53
src/service/api/request.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import axios from 'axios'
|
||||
import { BizError } from '@/types/core';
|
||||
import {getToken} from '@/service/user-store'
|
||||
|
||||
const axiosService = axios.create({
|
||||
baseURL: '/api/v2',
|
||||
timeout: 5000,
|
||||
headers: {
|
||||
'Authorization': getToken()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
export function request<T>(options: RequestOption) {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const { url, method, data, baseURL, getOriginResult } = options;
|
||||
|
||||
axiosService.request<APIResponse<T>>({
|
||||
url,
|
||||
method: method || 'get',
|
||||
data,
|
||||
baseURL,
|
||||
}).then(res => {
|
||||
if (res.status != 200) {
|
||||
reject(new BizError("Service Internal Exception,Please Try Later!", res.status))
|
||||
return;
|
||||
}
|
||||
if (getOriginResult) {
|
||||
resolve(res.data as unknown as T)
|
||||
return;
|
||||
}
|
||||
// const
|
||||
const { code, message, data } = res.data
|
||||
if (code == 0) {
|
||||
resolve(data as unknown as T)
|
||||
} else {
|
||||
reject(new BizError(message, code, data))
|
||||
}
|
||||
}).catch(e => {
|
||||
reject(new BizError(e.message, 500))
|
||||
})
|
||||
})
|
||||
}
|
||||
export function post<T>(url:string,data?: any){
|
||||
return request<T>({
|
||||
url,
|
||||
method:'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function get<T>(url:string){
|
||||
return request<T>({url})
|
||||
}
|
@ -1,29 +1,27 @@
|
||||
import {BizError} from "../../core/errors.ts";
|
||||
import { sleep } from "@/core/sleep";
|
||||
import { get, post } from "./request";
|
||||
|
||||
export type LoginModel = {
|
||||
account: string;
|
||||
password: string;
|
||||
|
||||
|
||||
export function userLogin(params: LoginModel) {
|
||||
return post<{ token: string; user: UserInfo }>(`/user/login`, params);
|
||||
}
|
||||
|
||||
const fakeUser: UserInfo = {
|
||||
account: "admin",
|
||||
nickname: "管理员",
|
||||
password: "",
|
||||
role: 'root',
|
||||
uid: 1
|
||||
export function getInfo() {
|
||||
return get<UserInfo>(`/user/info`);
|
||||
}
|
||||
|
||||
export async function LoginService(params: LoginModel) {
|
||||
if (params.account == 'admin' && params.password == 'admin') {
|
||||
const loginInfo = Date.now().toString(16);
|
||||
return {
|
||||
userinfo: fakeUser,
|
||||
token: loginInfo
|
||||
};
|
||||
}
|
||||
throw new BizError(1001, '用户名或密码错误');
|
||||
export async function getList(param: UserSearchParam) {
|
||||
await sleep(200);
|
||||
return await post<DataList<UserInfo>>(`/user/all`, param);
|
||||
}
|
||||
|
||||
export async function GetLoginInfo() {
|
||||
return fakeUser;
|
||||
export function deleteUser(id:number){
|
||||
return post(`/user/delete`,{id})
|
||||
}
|
||||
|
||||
export const columns = [
|
||||
{ title: '账号', dataIndex: 'account' },
|
||||
{ title: '姓名', dataIndex: 'nickname' },
|
||||
{ title: '角色', dataIndex: 'role' },
|
||||
]
|
@ -2,177 +2,221 @@ export type BaseModel = {
|
||||
id: number;
|
||||
name: string;
|
||||
alias?: string;
|
||||
}
|
||||
};
|
||||
export type ProductFieldValue = {
|
||||
id: number;
|
||||
alias?: string;
|
||||
product_id: number;
|
||||
field_id: number;
|
||||
value: number;
|
||||
}
|
||||
};
|
||||
export type ProductValue = {
|
||||
product: BaseModel;
|
||||
values: ProductFieldValue[]
|
||||
}
|
||||
values: ProductFieldValue[];
|
||||
};
|
||||
export const fields: BaseModel[] = [
|
||||
{id: 1, name: '能量密度', alias: 'power'},
|
||||
{id: 2, name: '蛋白Pro', alias: 'protein'},
|
||||
{id: 3, name: 'Glu', alias: 'glu'},
|
||||
{id: 4, name: 'Fat', alias: 'fat'},
|
||||
{id: 5, name: '纤维素', alias: 'cellulose'},
|
||||
{id: 6, name: 'Na', alias: 'na'},
|
||||
{id: 7, name: 'K', alias: 'k'},
|
||||
{id: 8, name: 'Ca', alias: 'ca'},
|
||||
{id: 9, name: 'P', alias: 'p'},
|
||||
{id: 10, name: 'Mg', alias: 'mg'},
|
||||
]
|
||||
{ id: 1, name: "能量密度", alias: "power" },
|
||||
{ id: 2, name: "蛋白Pro", alias: "protein" },
|
||||
{ id: 3, name: "Glu", alias: "glu" },
|
||||
{ id: 4, name: "Fat", alias: "fat" },
|
||||
{ id: 5, name: "纤维素", alias: "cellulose" },
|
||||
{ id: 6, name: "Na", alias: "na" },
|
||||
{ id: 7, name: "K", alias: "k" },
|
||||
{ id: 8, name: "Ca", alias: "ca" },
|
||||
{ id: 9, name: "P", alias: "p" },
|
||||
{ id: 10, name: "Mg", alias: "mg" },
|
||||
];
|
||||
export const products: BaseModel[] = [
|
||||
{id: 1, name: '爱伦多', alias: 'ailunduo'},
|
||||
{id: 2, name: '维沃', alias: 'weiwo'},
|
||||
{id: 3, name: '佳维体', alias: 'jiaweiti'},
|
||||
{id: 4, name: '伊力佳', alias: 'yilijia'},
|
||||
]
|
||||
{ id: 1, name: "爱伦多", alias: "ailunduo" },
|
||||
{ id: 2, name: "维沃", alias: "weiwo" },
|
||||
{ id: 3, name: "佳维体", alias: "jiaweiti" },
|
||||
{ id: 4, name: "伊力佳", alias: "yilijia" },
|
||||
];
|
||||
|
||||
const PRODUCT_VALUES_KEY = 'PRODUCT_VALUES_KEY'
|
||||
const PRODUCT_VALUES_KEY = "PRODUCT_VALUES_KEY";
|
||||
const product_values_default: ProductFieldValue[] = [
|
||||
{
|
||||
"id": 1,
|
||||
"product_id": 1,
|
||||
"field_id": 1,
|
||||
"value": 0,
|
||||
"alias": "ailunduo_power"
|
||||
}, {"id": 2, "product_id": 1, "field_id": 2, "value": 0, "alias": "ailunduo_protein"}, {
|
||||
"id": 3,
|
||||
"product_id": 1,
|
||||
"field_id": 3,
|
||||
"value": 0,
|
||||
"alias": "ailunduo_glu"
|
||||
}, {"id": 4, "product_id": 1, "field_id": 4, "value": 0, "alias": "ailunduo_fat"}, {
|
||||
"id": 5,
|
||||
"product_id": 1,
|
||||
"field_id": 5,
|
||||
"value": 0,
|
||||
"alias": "ailunduo_cellulose"
|
||||
}, {"id": 6, "product_id": 1, "field_id": 6, "value": 0, "alias": "ailunduo_na"}, {
|
||||
"id": 7,
|
||||
"product_id": 1,
|
||||
"field_id": 7,
|
||||
"value": 0,
|
||||
"alias": "ailunduo_k"
|
||||
}, {"id": 8, "product_id": 1, "field_id": 8, "value": 0, "alias": "ailunduo_ca"}, {
|
||||
"id": 9,
|
||||
"product_id": 1,
|
||||
"field_id": 9,
|
||||
"value": 0,
|
||||
"alias": "ailunduo_p"
|
||||
}, {"id": 10, "product_id": 1, "field_id": 10, "value": 0, "alias": "ailunduo_mg"}, {
|
||||
"id": 11,
|
||||
"product_id": 2,
|
||||
"field_id": 1,
|
||||
"value": 0,
|
||||
"alias": "weiwo_power"
|
||||
}, {"id": 12, "product_id": 2, "field_id": 2, "value": 0, "alias": "weiwo_protein"}, {
|
||||
"id": 13,
|
||||
"product_id": 2,
|
||||
"field_id": 3,
|
||||
"value": 0,
|
||||
"alias": "weiwo_glu"
|
||||
}, {"id": 14, "product_id": 2, "field_id": 4, "value": 0, "alias": "weiwo_fat"}, {
|
||||
"id": 15,
|
||||
"product_id": 2,
|
||||
"field_id": 5,
|
||||
"value": 0,
|
||||
"alias": "weiwo_cellulose"
|
||||
}, {"id": 16, "product_id": 2, "field_id": 6, "value": 0, "alias": "weiwo_na"}, {
|
||||
"id": 17,
|
||||
"product_id": 2,
|
||||
"field_id": 7,
|
||||
"value": 0,
|
||||
"alias": "weiwo_k"
|
||||
}, {"id": 18, "product_id": 2, "field_id": 8, "value": 0, "alias": "weiwo_ca"}, {
|
||||
"id": 19,
|
||||
"product_id": 2,
|
||||
"field_id": 9,
|
||||
"value": 0,
|
||||
"alias": "weiwo_p"
|
||||
}, {"id": 20, "product_id": 2, "field_id": 10, "value": 0, "alias": "weiwo_mg"}, {
|
||||
"id": 21,
|
||||
"product_id": 3,
|
||||
"field_id": 1,
|
||||
"value": 0,
|
||||
"alias": "jiaweiti_power"
|
||||
}, {"id": 22, "product_id": 3, "field_id": 2, "value": 0, "alias": "jiaweiti_protein"}, {
|
||||
"id": 23,
|
||||
"product_id": 3,
|
||||
"field_id": 3,
|
||||
"value": 0,
|
||||
"alias": "jiaweiti_glu"
|
||||
}, {"id": 24, "product_id": 3, "field_id": 4, "value": 0, "alias": "jiaweiti_fat"}, {
|
||||
"id": 25,
|
||||
"product_id": 3,
|
||||
"field_id": 5,
|
||||
"value": 0,
|
||||
"alias": "jiaweiti_cellulose"
|
||||
}, {"id": 26, "product_id": 3, "field_id": 6, "value": 0, "alias": "jiaweiti_na"}, {
|
||||
"id": 27,
|
||||
"product_id": 3,
|
||||
"field_id": 7,
|
||||
"value": 0,
|
||||
"alias": "jiaweiti_k"
|
||||
}, {"id": 28, "product_id": 3, "field_id": 8, "value": 0, "alias": "jiaweiti_ca"}, {
|
||||
"id": 29,
|
||||
"product_id": 3,
|
||||
"field_id": 9,
|
||||
"value": 0,
|
||||
"alias": "jiaweiti_p"
|
||||
}, {"id": 30, "product_id": 3, "field_id": 10, "value": 0, "alias": "jiaweiti_mg"}, {
|
||||
"id": 31,
|
||||
"product_id": 4,
|
||||
"field_id": 1,
|
||||
"value": 0,
|
||||
"alias": "yilijia_power"
|
||||
}, {"id": 32, "product_id": 4, "field_id": 2, "value": 0, "alias": "yilijia_protein"}, {
|
||||
"id": 33,
|
||||
"product_id": 4,
|
||||
"field_id": 3,
|
||||
"value": 0,
|
||||
"alias": "yilijia_glu"
|
||||
}, {"id": 34, "product_id": 4, "field_id": 4, "value": 0, "alias": "yilijia_fat"}, {
|
||||
"id": 35,
|
||||
"product_id": 4,
|
||||
"field_id": 5,
|
||||
"value": 0,
|
||||
"alias": "yilijia_cellulose"
|
||||
}, {"id": 36, "product_id": 4, "field_id": 6, "value": 0, "alias": "yilijia_na"}, {
|
||||
"id": 37,
|
||||
"product_id": 4,
|
||||
"field_id": 7,
|
||||
"value": 0,
|
||||
"alias": "yilijia_k"
|
||||
}, {"id": 38, "product_id": 4, "field_id": 8, "value": 0, "alias": "yilijia_ca"}, {
|
||||
"id": 39,
|
||||
"product_id": 4,
|
||||
"field_id": 9,
|
||||
"value": 0,
|
||||
"alias": "yilijia_p"
|
||||
}, {"id": 40, "product_id": 4, "field_id": 10, "value": 0, "alias": "yilijia_mg"}]
|
||||
|
||||
id: 1,
|
||||
product_id: 1,
|
||||
field_id: 1,
|
||||
value: 0,
|
||||
alias: "ailunduo_power",
|
||||
},
|
||||
{ id: 2, product_id: 1, field_id: 2, value: 0, alias: "ailunduo_protein" },
|
||||
{
|
||||
id: 3,
|
||||
product_id: 1,
|
||||
field_id: 3,
|
||||
value: 0,
|
||||
alias: "ailunduo_glu",
|
||||
},
|
||||
{ id: 4, product_id: 1, field_id: 4, value: 0, alias: "ailunduo_fat" },
|
||||
{
|
||||
id: 5,
|
||||
product_id: 1,
|
||||
field_id: 5,
|
||||
value: 0,
|
||||
alias: "ailunduo_cellulose",
|
||||
},
|
||||
{ id: 6, product_id: 1, field_id: 6, value: 0, alias: "ailunduo_na" },
|
||||
{
|
||||
id: 7,
|
||||
product_id: 1,
|
||||
field_id: 7,
|
||||
value: 0,
|
||||
alias: "ailunduo_k",
|
||||
},
|
||||
{ id: 8, product_id: 1, field_id: 8, value: 0, alias: "ailunduo_ca" },
|
||||
{
|
||||
id: 9,
|
||||
product_id: 1,
|
||||
field_id: 9,
|
||||
value: 0,
|
||||
alias: "ailunduo_p",
|
||||
},
|
||||
{ id: 10, product_id: 1, field_id: 10, value: 0, alias: "ailunduo_mg" },
|
||||
{
|
||||
id: 11,
|
||||
product_id: 2,
|
||||
field_id: 1,
|
||||
value: 0,
|
||||
alias: "weiwo_power",
|
||||
},
|
||||
{ id: 12, product_id: 2, field_id: 2, value: 0, alias: "weiwo_protein" },
|
||||
{
|
||||
id: 13,
|
||||
product_id: 2,
|
||||
field_id: 3,
|
||||
value: 0,
|
||||
alias: "weiwo_glu",
|
||||
},
|
||||
{ id: 14, product_id: 2, field_id: 4, value: 0, alias: "weiwo_fat" },
|
||||
{
|
||||
id: 15,
|
||||
product_id: 2,
|
||||
field_id: 5,
|
||||
value: 0,
|
||||
alias: "weiwo_cellulose",
|
||||
},
|
||||
{ id: 16, product_id: 2, field_id: 6, value: 0, alias: "weiwo_na" },
|
||||
{
|
||||
id: 17,
|
||||
product_id: 2,
|
||||
field_id: 7,
|
||||
value: 0,
|
||||
alias: "weiwo_k",
|
||||
},
|
||||
{ id: 18, product_id: 2, field_id: 8, value: 0, alias: "weiwo_ca" },
|
||||
{
|
||||
id: 19,
|
||||
product_id: 2,
|
||||
field_id: 9,
|
||||
value: 0,
|
||||
alias: "weiwo_p",
|
||||
},
|
||||
{ id: 20, product_id: 2, field_id: 10, value: 0, alias: "weiwo_mg" },
|
||||
{
|
||||
id: 21,
|
||||
product_id: 3,
|
||||
field_id: 1,
|
||||
value: 0,
|
||||
alias: "jiaweiti_power",
|
||||
},
|
||||
{ id: 22, product_id: 3, field_id: 2, value: 0, alias: "jiaweiti_protein" },
|
||||
{
|
||||
id: 23,
|
||||
product_id: 3,
|
||||
field_id: 3,
|
||||
value: 0,
|
||||
alias: "jiaweiti_glu",
|
||||
},
|
||||
{ id: 24, product_id: 3, field_id: 4, value: 0, alias: "jiaweiti_fat" },
|
||||
{
|
||||
id: 25,
|
||||
product_id: 3,
|
||||
field_id: 5,
|
||||
value: 0,
|
||||
alias: "jiaweiti_cellulose",
|
||||
},
|
||||
{ id: 26, product_id: 3, field_id: 6, value: 0, alias: "jiaweiti_na" },
|
||||
{
|
||||
id: 27,
|
||||
product_id: 3,
|
||||
field_id: 7,
|
||||
value: 0,
|
||||
alias: "jiaweiti_k",
|
||||
},
|
||||
{ id: 28, product_id: 3, field_id: 8, value: 0, alias: "jiaweiti_ca" },
|
||||
{
|
||||
id: 29,
|
||||
product_id: 3,
|
||||
field_id: 9,
|
||||
value: 0,
|
||||
alias: "jiaweiti_p",
|
||||
},
|
||||
{ id: 30, product_id: 3, field_id: 10, value: 0, alias: "jiaweiti_mg" },
|
||||
{
|
||||
id: 31,
|
||||
product_id: 4,
|
||||
field_id: 1,
|
||||
value: 0,
|
||||
alias: "yilijia_power",
|
||||
},
|
||||
{ id: 32, product_id: 4, field_id: 2, value: 0, alias: "yilijia_protein" },
|
||||
{
|
||||
id: 33,
|
||||
product_id: 4,
|
||||
field_id: 3,
|
||||
value: 0,
|
||||
alias: "yilijia_glu",
|
||||
},
|
||||
{ id: 34, product_id: 4, field_id: 4, value: 0, alias: "yilijia_fat" },
|
||||
{
|
||||
id: 35,
|
||||
product_id: 4,
|
||||
field_id: 5,
|
||||
value: 0,
|
||||
alias: "yilijia_cellulose",
|
||||
},
|
||||
{ id: 36, product_id: 4, field_id: 6, value: 0, alias: "yilijia_na" },
|
||||
{
|
||||
id: 37,
|
||||
product_id: 4,
|
||||
field_id: 7,
|
||||
value: 0,
|
||||
alias: "yilijia_k",
|
||||
},
|
||||
{ id: 38, product_id: 4, field_id: 8, value: 0, alias: "yilijia_ca" },
|
||||
{
|
||||
id: 39,
|
||||
product_id: 4,
|
||||
field_id: 9,
|
||||
value: 0,
|
||||
alias: "yilijia_p",
|
||||
},
|
||||
{ id: 40, product_id: 4, field_id: 10, value: 0, alias: "yilijia_mg" },
|
||||
];
|
||||
|
||||
export function getProductValues() {
|
||||
const sessionData = localStorage.getItem(PRODUCT_VALUES_KEY)
|
||||
const product_values:ProductFieldValue[] =sessionData?JSON.parse(sessionData):product_values_default;
|
||||
const values: Record<number, ProductFieldValue[]> = {}
|
||||
product_values.forEach(s => {
|
||||
if(!values[s.product_id]) values[s.product_id]= [];
|
||||
const sessionData = localStorage.getItem(PRODUCT_VALUES_KEY);
|
||||
const product_values: ProductFieldValue[] = sessionData
|
||||
? JSON.parse(sessionData)
|
||||
: product_values_default;
|
||||
const values: Record<number, ProductFieldValue[]> = {};
|
||||
product_values.forEach((s) => {
|
||||
if (!values[s.product_id]) values[s.product_id] = [];
|
||||
values[s.product_id].push(s);
|
||||
})
|
||||
const results: ProductValue[] = products.map(product=>({product,values: values[product.id]}));
|
||||
});
|
||||
const results: ProductValue[] = products.map((product) => ({
|
||||
product,
|
||||
values: values[product.id],
|
||||
}));
|
||||
return results;
|
||||
}
|
||||
|
||||
export function saveProductValues(datas: ProductValue[]){
|
||||
const values = datas.map(s=>s.values).flatMap(s=>s)
|
||||
localStorage.setItem(PRODUCT_VALUES_KEY,JSON.stringify(values))
|
||||
export function saveProductValues(datas: ProductValue[]) {
|
||||
const values = datas.map((s) => s.values).flatMap((s) => s);
|
||||
localStorage.setItem(PRODUCT_VALUES_KEY, JSON.stringify(values));
|
||||
}
|
||||
|
||||
// const product_values: ProductFieldValue[] = []
|
||||
@ -189,4 +233,3 @@ export function saveProductValues(datas: ProductValue[]){
|
||||
// })
|
||||
// })
|
||||
// console.log(JSON.stringify(product_values))
|
||||
|
||||
|
47
src/service/useRequest.ts
Normal file
47
src/service/useRequest.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
|
||||
export default function useRequest<T>(
|
||||
service: () => Promise<T>,
|
||||
options?: {
|
||||
manual?: boolean;
|
||||
refreshDeps?: any[];
|
||||
}
|
||||
) {
|
||||
const data = ref<T>();
|
||||
const loading = ref(false);
|
||||
const error = ref<string>();
|
||||
|
||||
const _request = () => {
|
||||
loading.value = true
|
||||
return service()
|
||||
.then((res) => {
|
||||
data.value = res
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
error.value = e.message
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
const refresh = () => {
|
||||
_request()
|
||||
}
|
||||
onMounted(() => {
|
||||
if (!options?.manual) {
|
||||
_request()
|
||||
}
|
||||
});
|
||||
if (options?.refreshDeps) {
|
||||
watch(options.refreshDeps, () => {
|
||||
_request()
|
||||
})
|
||||
}
|
||||
return {
|
||||
data,
|
||||
refresh,
|
||||
loading,
|
||||
error,
|
||||
run: _request
|
||||
}
|
||||
}
|
@ -1,15 +1,20 @@
|
||||
import {defineStore} from "pinia"
|
||||
import {onMounted, ref} from "vue"
|
||||
import {GetLoginInfo, LoginService} from "./api/user";
|
||||
import {BizError} from "../core/errors.ts";
|
||||
import {sleep} from "../core/sleep.ts";
|
||||
import { defineStore } from "pinia"
|
||||
import { onMounted, ref } from "vue"
|
||||
import { md5 } from "js-md5";
|
||||
import { sleep } from "@/core/sleep.ts";
|
||||
import { userLogin, getInfo } from "./api/user";
|
||||
|
||||
import { BizError } from "@/types/core.ts";
|
||||
|
||||
type LoginParam = {
|
||||
account: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export const LOGIN_SESSION_KEY = 'x-yy-js-data-user';
|
||||
const LOGIN_SESSION_KEY = 'x-yy-js-data-user';
|
||||
export function getToken() {
|
||||
return localStorage.getItem('x-yy-js-data-user')
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore('counter', () => {
|
||||
const userInfo = ref<UserInfo>()
|
||||
@ -17,15 +22,17 @@ export const useUserStore = defineStore('counter', () => {
|
||||
|
||||
// 登录
|
||||
const login = async (data: LoginParam) => {
|
||||
await sleep(1000);
|
||||
const info = await LoginService(data);
|
||||
userInfo.value = info.userinfo;
|
||||
await sleep(500);
|
||||
const info = await userLogin({
|
||||
...data,
|
||||
password: md5(data.password),
|
||||
});
|
||||
userInfo.value = info.user;
|
||||
localStorage.setItem(LOGIN_SESSION_KEY, info.token)
|
||||
}
|
||||
|
||||
// 登出
|
||||
const logout = async () => {
|
||||
await sleep(1000);
|
||||
localStorage.removeItem(LOGIN_SESSION_KEY)
|
||||
userInfo.value = undefined;
|
||||
}
|
||||
@ -37,9 +44,9 @@ export const useUserStore = defineStore('counter', () => {
|
||||
const getUserInfo = async () => {
|
||||
const token = localStorage.getItem(LOGIN_SESSION_KEY);
|
||||
if (!token) {
|
||||
throw new BizError(401)
|
||||
throw new BizError('need token', 401)
|
||||
}
|
||||
userInfo.value = await GetLoginInfo();
|
||||
userInfo.value = await getInfo();
|
||||
}
|
||||
onMounted(() => {
|
||||
|
||||
@ -50,9 +57,9 @@ export const useUserStore = defineStore('counter', () => {
|
||||
}
|
||||
}).finally(() => {
|
||||
console.log('onMounted inited')
|
||||
setTimeout(()=>{
|
||||
setTimeout(() => {
|
||||
userInit.value = true
|
||||
},500)
|
||||
}, 500)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
@use "./assets/libs" as *;
|
||||
|
||||
@font-face {
|
||||
font-family: 'iconfont';
|
||||
/* Project id 4404323 */
|
||||
@ -166,6 +168,11 @@ img {
|
||||
}
|
||||
|
||||
.ant-btn {
|
||||
.anticon {
|
||||
position: relative;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
&.btn-info {
|
||||
background-color: var(--primary-color-2);
|
||||
border-color: var(--primary-color-2);
|
||||
@ -179,31 +186,81 @@ img {
|
||||
}
|
||||
}
|
||||
|
||||
.layout{
|
||||
.menu{
|
||||
min-width: 100px;
|
||||
}
|
||||
}
|
||||
.menu-text{
|
||||
display: block;
|
||||
@include media-breakpoint-down(lg) {
|
||||
font-size: 12px;;
|
||||
}
|
||||
@include media-breakpoint-down(md) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.search-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 20px 0;
|
||||
|
||||
.form-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.search-form {
|
||||
|
||||
.ant-input,
|
||||
.ant-select .ant-select-selector {
|
||||
background-color: #f7f8fa;
|
||||
border: solid 1px transparent;
|
||||
min-width: 100px;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
border-color: var(--primary-color-1-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-result-table {}
|
||||
|
||||
.data-page {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.table {
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
th,td{
|
||||
|
||||
th,
|
||||
td {
|
||||
min-width: 0;
|
||||
height: 42px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
border-bottom: 1px solid #e8e8ec;
|
||||
padding: 0 16px;
|
||||
padding: 15px 16px;
|
||||
}
|
||||
|
||||
th {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
background-color: #f2f3f5;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
padding:10px 16px;
|
||||
}
|
||||
tr{
|
||||
&:hover{
|
||||
|
||||
tr {
|
||||
&:hover {
|
||||
background-color: #ebf7ff;
|
||||
}
|
||||
}
|
||||
|
26
src/types/api.d.ts
vendored
Normal file
26
src/types/api.d.ts
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// 请求方式
|
||||
declare type RequestMethod = 'get' | 'post' | 'put' | 'delete'
|
||||
declare type RequestOption = {
|
||||
url: string;
|
||||
method?: RequestMethod;
|
||||
data?: AllType | null;
|
||||
getOriginResult?: boolean;
|
||||
baseURL?: string;
|
||||
}
|
||||
|
||||
// 接口返回数据类型
|
||||
declare interface APIResponse<T> {
|
||||
/**
|
||||
* 错误码,0:成功,其他失败
|
||||
*/
|
||||
code: number;
|
||||
/**
|
||||
* 非0情况下,提示信息
|
||||
*/
|
||||
message: string;
|
||||
data?: T;
|
||||
}
|
||||
declare interface DataList<T>{
|
||||
list: T[];
|
||||
total: number;
|
||||
}
|
13
src/types/core.ts
Normal file
13
src/types/core.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export class BizError extends Error {
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
code = 1;
|
||||
data: any;
|
||||
|
||||
constructor(message: string, code = 1,data?: any) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
23
src/types/user.d.ts
vendored
Normal file
23
src/types/user.d.ts
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
declare type LoginModel = {
|
||||
account: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
declare type UserSearchParam = {
|
||||
page: number;
|
||||
limit: number;
|
||||
nickname?: string;
|
||||
role?: string;
|
||||
account?: string;
|
||||
}
|
||||
declare interface UserInfo {
|
||||
id: number;
|
||||
nickname: string;
|
||||
account: string;
|
||||
password: string;
|
||||
role: string;
|
||||
created_at: string;
|
||||
last_login: string;
|
||||
updated_at: string;
|
||||
status: number;
|
||||
}
|
11
src/vite-env.d.ts
vendored
11
src/vite-env.d.ts
vendored
@ -3,14 +3,3 @@
|
||||
type int = number;
|
||||
type double = number;
|
||||
type bool = boolean;
|
||||
type AccountRole = 'root' | 'admin' | 'user';
|
||||
type UserInfo = {
|
||||
uid: number;
|
||||
nickname: string;
|
||||
account: string;
|
||||
password: string;
|
||||
avatar?: string;
|
||||
lastUpdate?:string;
|
||||
createTime?: string;
|
||||
role: AccountRole;
|
||||
}
|
56
tailwind.config.js
Normal file
56
tailwind.config.js
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
const themeConfig = {
|
||||
colors: {
|
||||
'primary': '#7356f6',
|
||||
'primary-bg': '#f6f6f6',
|
||||
'active': '#FFE0E0',
|
||||
'primary-red': '#F5222D',
|
||||
'primary-red-70': 'rgba(245,34,45,0.7)',
|
||||
},
|
||||
widths: {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
'./index.html',
|
||||
'./src/**/*.{mjs,js,ts,jsx,tsx,html,vue}'
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
width: {
|
||||
'396px': '396px',
|
||||
'1200px': '1200px',
|
||||
'1000px': '1000px',
|
||||
'chat-input': '800px',
|
||||
...themeConfig.widths,
|
||||
},
|
||||
margin: {
|
||||
...themeConfig.widths,
|
||||
},
|
||||
padding: {
|
||||
'basic': '20px',
|
||||
...themeConfig.widths,
|
||||
},
|
||||
color: {
|
||||
...themeConfig.colors,
|
||||
},
|
||||
borderColor: {
|
||||
...themeConfig.colors,
|
||||
},
|
||||
backgroundColor: {
|
||||
...themeConfig.colors,
|
||||
}
|
||||
},
|
||||
screens: {
|
||||
sm: '768px',
|
||||
md: '1024px',
|
||||
lg: '1200px',
|
||||
xl: '1440px',
|
||||
}
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
@ -13,6 +13,11 @@
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
},
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
|
@ -26,9 +26,18 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
base: './',
|
||||
// resolve:{
|
||||
// alias:{
|
||||
// '@': path.resolve(__dirname,"./src")
|
||||
// }
|
||||
// },
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src')
|
||||
}
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:3001',
|
||||
changeOrigin: true,
|
||||
// rewrite: path => path.replace(/^\/api/, '')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user