添加产品(药品)数据(修改数据存储模式);修复useRequest使用

This commit is contained in:
LittleBoy 2024-12-20 23:24:44 +08:00
parent f57daee407
commit a740a36953
19 changed files with 450 additions and 244 deletions

View File

@ -16,7 +16,7 @@ const themeConfig = {
<template> <template>
<div class="app-container"> <div class="app-container">
<PageLoading :class="{ hideLoading: store.userInit }" /> <PageLoading :class="{ hideLoading: store.userInit }" />
<Login v-if="store.userInit && (!store.userInfo || store.userInfo.uid < 1)" /> <Login v-if="store.userInit && (!store.userInfo || store.userInfo.id < 1)" />
<template v-if="store.userInit"> <template v-if="store.userInit">
<ConfigProvider :theme="themeConfig"> <ConfigProvider :theme="themeConfig">
<router-view /> <router-view />

View File

@ -27,3 +27,14 @@
} }
} }
} }
.ant-modal {
.ant-btn-dangerous {
background-color: hsl(359, 100%, 60%);
color: white !important;
&:hover {
background-color: hsl(359, 100%, 70%);
}
}
}

View File

@ -6,6 +6,7 @@ export enum RoleEnum {
// common user // common user
USER = 'user', USER = 'user',
} }
export const RoleList = [ export const RoleList = [
{ label: '超级管理员', value: RoleEnum.ROOT }, { label: '超级管理员', value: RoleEnum.ROOT },
{ label: '管理员', value: RoleEnum.ADMIN }, { label: '管理员', value: RoleEnum.ADMIN },
@ -15,3 +16,16 @@ export const AllRoleList = [
{ label: '全部', value: '' }, { label: '全部', value: '' },
...RoleList ...RoleList
] ]
export enum ProductCategoryEnum {
GUT = 'gut',
VEIN = 'vein'
}
export const ProductCategoryList = [
{ label: '肠内制剂', value: ProductCategoryEnum.GUT },
{ label: '静脉制剂', value: ProductCategoryEnum.VEIN }
]
export const AllProductCategoryList = [
{ label: '全部', value: '' },
...ProductCategoryList
]

View File

@ -4,12 +4,17 @@ import { useUserStore } from "../service/user-store.ts";
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import { AppConfig } from "../app-config.ts"; import { AppConfig } from "../app-config.ts";
import { Dropdown, Menu, MenuItem, Button } from "ant-design-vue"; import { Dropdown, Menu, MenuItem, Button, Modal, Form, Input, message } from "ant-design-vue";
import { DownOutlined } from "@ant-design/icons-vue" import { CaretDownOutlined } from "@ant-design/icons-vue"
import { MenuInfo } from "ant-design-vue/es/menu/src/interface"; import { MenuInfo } from "ant-design-vue/es/menu/src/interface";
// //
const store = useUserStore() const store = useUserStore()
const showLogo = ref(false) const showLogo = ref(false)
const showUpdateModal = ref(false)
const modifyLoading = ref(false)
// 访 // 访
const currentMenus = computed(() => { const currentMenus = computed(() => {
// //
@ -18,14 +23,45 @@ const currentMenus = computed(() => {
return !s.meta || !s.meta['role'] || store.userInfo?.role == s.meta.role; return !s.meta || !s.meta['role'] || store.userInfo?.role == s.meta.role;
}) })
}) })
const handleMenuClick = ({ key }: MenuInfo) => { const handleMenuClick = ({ key }: MenuInfo) => {
console.log('click menu', key) console.log('click menu', key)
if (key == 'logout') { if (key == 'logout') {
store.logout() store.logout()
} else if (key == 'logout') { } else if (key == 'modifyPassword') {
showUpdateModal.value = true
} }
} }
const editData = ref<ModifyUserPassword>({
origin_password: '',
new_password: '',
repeat_password: ''
})
const handleUpdatePassword = () => {
modifyLoading.value = true
store.updatePassword(editData.value).then(() => {
message.success('修改登录密码成功')
}).finally(() => {
showUpdateModal.value = false
})
}
const validateConfirmPass = async (_rule: any, value: string) => {
if (value === '') {
return Promise.reject('请再次输入新密码确认!');
} else if (value !== editData.value.new_password) {
return Promise.reject("两次输入的密码不一致");
} else if (value === editData.value.origin_password) {
return Promise.reject("新密码不能与原始密码相同");
}
return Promise.resolve();
};
function handleHideModal() {
//
showUpdateModal.value = false
//
editData.value = { origin_password: '', new_password: '', repeat_password: '' }
}
</script> </script>
<template> <template>
@ -53,13 +89,13 @@ const handleMenuClick = ({ key }: MenuInfo) => {
<Dropdown> <Dropdown>
<template #overlay> <template #overlay>
<Menu @click="handleMenuClick"> <Menu @click="handleMenuClick">
<MenuItem key="modifyPassword">修改资料</MenuItem> <MenuItem key="modifyPassword">修改密码</MenuItem>
<MenuItem key="logout">退出登录</MenuItem> <MenuItem key="logout">退出登录</MenuItem>
</Menu> </Menu>
</template> </template>
<Button class="flex item-center"> <Button class="flex items-center">
<span>{{ store.userInfo?.nickname }}</span> <span>{{ store.userInfo?.nickname }}</span>
<DownOutlined /> <CaretDownOutlined :style="{ fontSize: '12px' }" />
</Button> </Button>
</Dropdown> </Dropdown>
</div> </div>
@ -68,6 +104,30 @@ const handleMenuClick = ({ key }: MenuInfo) => {
<router-view /> <router-view />
</div> </div>
</div> </div>
<Modal :width="450" :open="showUpdateModal" title="修改登录密码" :mask-closable="false" :destroy-on-close="true"
@cancel="handleHideModal" :footer="null">
<Form @finish="handleUpdatePassword" autocomplete="off" :model="editData" :label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }">
<div class="py-8">
<Form.Item label="原始密码" name="origin_password" :rules="[{ required: true, message: '请输入原始密码' }]">
<Input type="password" placeholder="请输入原始密码" v-model:value="editData.origin_password" />
</Form.Item>
<Form.Item label="新密码" name="new_password"
:rules="[{ required: true, message: '请输入新密码' }, { min: 6, max: 12, message: '密码为6-12位长度' }]">
<Input type="password" placeholder="请输入新密码" v-model:value="editData.new_password" />
</Form.Item>
<Form.Item label="确认密码" name="repeat_password" :rules="[{ validator: validateConfirmPass }]">
<Input type="password" placeholder="请输入新密码确认" v-model:value="editData.repeat_password" />
</Form.Item>
</div>
<Form.Item class="flex justify-end mb-0">
<div class="flex gap-4">
<Button @click="handleHideModal">取消</Button>
<Button type="primary" :loading="modifyLoading" html-type="submit">确定</Button>
</div>
</Form.Item>
</Form>
</Modal>
</div> </div>
</template> </template>

View File

@ -1,82 +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,
} from 'ant-design-vue'
import PageHeader from '../components/page-header.vue'
import { fields, getProductValues } from "../service/data";
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 width="150">操作</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"></style>

View File

@ -0,0 +1,133 @@
<script lang="ts" setup>
import { defineModel, reactive } from 'vue';
import { Modal, Form, Input, Space, Button, Select, SelectOption } from 'ant-design-vue';
import useRequest from '@/service/useRequest';
import { saveProduct } from '@/service/api/product';
const editData = defineModel<Partial<ProductInfoModel>>();
const currentData = reactive<Partial<ProductInfoModel>>({ ...(editData ? editData.value : {}) });
defineExpose({})
const emit = defineEmits<{
(e: 'saved'): void
}>()
const { loading, runAsync, error } = useRequest(saveProduct, {
manual: true
})
function handleSave() {
runAsync(currentData).then(() => {
editData.value = undefined;
emit('saved')
})
//
}
function handleCancel() {
editData.value = undefined;
}
function handleFinishError(errorInfo: any) {
console.log('handleFinishError', errorInfo)
}
function onValuesChange() {
error.value = undefined
}
/*
[
{ 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" },
];
*/
// const DataFields = [
// { label: '', name: 'power',message:'' },
// { label: 'Pro', name: 'protein',message:'Pro' },
// { label: 'Glu', name: 'glu',message:'Glu' },
// { label: 'Fat', name: 'fat',message:'Fat' },
// { label: '', name: 'cellulose',message:'' },
// { label: 'Na', name: 'na',message:'Na' },
// { label: 'K', name: 'k',message:'K' },
// { label: 'Ca', name: 'ca',message:'Ca' },
// { label: 'P', name: 'p',message:'P' },
// { label: 'Mg', name: 'mg',message:'Mg' },
// ]
</script>
<template>
<Modal :width="640" :open="true" :footer="null" @cancel="handleCancel" :mask-closable="false"
:destroy-on-close="true">
<div class="pt-12">
<Form @finish="handleSave" @finishFailed="handleFinishError" @validate="onValuesChange" autocomplete="off"
:model="currentData" :label-col="{ span: 7 }" :wrapper-col="{ span: 16 }">
<div class="form-item-container grid grid-cols-2">
<Form.Item label="营养制剂" name="name" :rules="[{ required: true, message: '请输入营养制剂名称' }]">
<Input placeholder="请输入营养制剂名称" v-model:value="currentData.name" />
</Form.Item>
<Form.Item label="别名" name="alias" :rules="[{ required: true, message: '请输入制剂别名' }]">
<Input placeholder="请输入别名" v-model:value="currentData.alias" />
</Form.Item>
<Form.Item label="单位" name="unit" :rules="[{ required: true, message: '请输入制剂单位' }]">
<Input placeholder="请输入单位" v-model:value="currentData.unit" />
</Form.Item>
<Form.Item label="分类" name="category" :rules="[{ required: true, message: '请输入分类' }]">
<Select v-model:value="currentData.category" placeholder="请选择制剂分类">
<SelectOption value="gut">肠内制剂</SelectOption>
<SelectOption value="vein">静脉制剂</SelectOption>
</Select>
</Form.Item>
<Form.Item label="能量密度" name="power" :rules="[{ required: true, message: '请输入能量密度' }]">
<Input placeholder="请输入能量密度" v-model:value="currentData.power" />
</Form.Item>
<Form.Item label="蛋白Pro" name="protein" :rules="[{ required: true, message: '请输入蛋白Pro' }]">
<Input placeholder="请输入蛋白Pro" v-model:value="currentData.protein" />
</Form.Item>
<Form.Item label="Glu" name="glu" :rules="[{ required: true, message: '请输入Glu' }]">
<Input placeholder="请输入Glu" v-model:value="currentData.glu" />
</Form.Item>
<Form.Item label="Fat" name="fat" :rules="[{ required: true, message: '请输入Fat' }]">
<Input placeholder="请输入Fat" v-model:value="currentData.fat" />
</Form.Item>
<Form.Item label="纤维素" name="cellulose" :rules="[{ required: true, message: '请输入纤维素' }]">
<Input placeholder="请输入纤维素" v-model:value="currentData.cellulose" />
</Form.Item>
<Form.Item label="Na" name="na" :rules="[{ required: true, message: '请输入Na' }]">
<Input placeholder="请输入Na" v-model:value="currentData.na" />
</Form.Item>
<Form.Item label="K" name="k" :rules="[{ required: true, message: '请输入K' }]">
<Input placeholder="请输入K" v-model:value="currentData.k" />
</Form.Item>
<Form.Item label="Ca" name="ca" :rules="[{ required: true, message: '请输入Ca' }]">
<Input placeholder="请输入Ca" v-model:value="currentData.ca" />
</Form.Item>
<Form.Item label="P" name="p" :rules="[{ required: true, message: '请输入P' }]">
<Input placeholder="请输入P" v-model:value="currentData.p" />
</Form.Item>
<Form.Item label="Mg" name="mg" :rules="[{ required: true, message: '请输入Mg' }]">
<Input placeholder="请输入Mg" v-model:value="currentData.mg" />
</Form.Item>
</div>
<!-- <Form.Item v-for="(item,index) in DataFields" :key="index"" :label="item.label" :name="item.name" :rules="[{ required: true, message: item.message }]">
<Input :placeholder="item.message" v-model:value="currentData.name" />
</Form.Item> -->
<div v-if="error" class="text-red-500 text-center">
{{ error?.message }}
</div>
<Form.Item class="flex justify-end mt-6 mb-0">
<Space :size="20">
<Button @click="handleCancel">取消</Button>
<Button :loading="loading" html-type="submit" class="btn-info">确定</Button>
</Space>
</Form.Item>
</Form>
</div>
</Modal>
</template>

112
src/pages/product/index.vue Normal file
View File

@ -0,0 +1,112 @@
<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 PageHeader from '@/components/page-header.vue'
import { AllProductCategoryList } from "@/core/enums";
import { ProductCols } from "@/service/api/product";
import { getList, deleteProduct } from "@/service/api/product";
import useRequest from "@/service/useRequest";
import EditModal from "./edit-modal.vue"
const editProduct = ref<Partial<ProductInfoModel>>()
const searchParams = ref<ProductSearchParam>({
page: 1,
limit: 10,
category: '',
})
const { data: allDataList, loading, run, refresh } = useRequest(() => getList(searchParams.value))
function deleteById(id: number) {
deleteProduct(id).then(() => {
message.success('删除成功')
refresh()
})
}
function handleDelete(id: number) {
Modal.confirm({
title: '删除营养制剂数据',
content:'删除此数据后可能导致系统无法正常计算结果,请确定要删除该数据吗?',
cancelText: '取消',
okText: '删除',
okType: 'danger',
onOk: () => {
deleteById(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.name" />
</Space>
<Space class="form-item">
<span>类型</span>
<Select class="search-item w-[120px]" v-model:value="searchParams.category">
<SelectOption v-for="(it, opIndex) in AllProductCategoryList" :key="opIndex" :value="it.value">
{{ it.label }}
</SelectOption>
</Select>
</Space>
<Space class="form-item" :size="15">
<Button :icon="h(SearchOutlined)" type="primary" @click="run">查询</Button>
<Button :icon="h(PlusOutlined)" class="btn-info" @click="editProduct = { id: 0 }">新增</Button>
</Space>
</Space>
</div>
<div class="search-result-table">
<div>
<Spin :spinning="loading">
<table class="table">
<thead>
<tr>
<th>营养制剂</th>
<th v-for="th in ProductCols">{{ th.name }}</th>
<th width="150">计量单位</th>
<th width="150">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(tr, rowIndex) in allDataList?.list" :key="rowIndex">
<td>{{ tr.name }}</td>
<td v-for="th in ProductCols">{{ (tr as any)[th.alias] || 'NULL' }}</td>
<td>{{ tr.unit }}</td>
<td>
<Space :size="20">
<a @click="editProduct = tr">编辑</a>
<a @click="handleDelete(tr.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} 条`" @change="run" />
</div>
</div>
<EditModal v-if="!!editProduct" v-model="editProduct" @saved="refresh" />
</div>
</template>
<style lang="scss"></style>

View File

@ -1,77 +0,0 @@
<template>
<p>输出计算</p>
<!-- <DataField /> -->
<Button @click="showMessage">test</Button>
<div>
<Demo />
</div>
<div>
<Form @finish="handleSave" @finishFailed="handleFinishError" autocomplete="off" :model="userData"
:label-col="{ span: 5 }" :wrapper-col="{ span: 18 }">
<Form.Item label="用户名" name="nickname" :rules="[{ required: true, message: '请输入姓名' }]">
<Input placeholder="请输入姓名" v-model:value="userData.nickname" />
</Form.Item>
<Form.Item label="账号" name="account" :rules="[{ required: true, message: '请输入手机号或邮箱' }]">
<Input placeholder="请输入手机号或邮箱" v-model:value="userData.account" />
</Form.Item>
<Form.Item label="密码" name="password" :rules="passwordRules">
<Input placeholder="请输入6-12位密码" type="password" v-model:value="userData.password" />
<div>{{ userData?.id ? '如果不需要修改,请不要填写' : '' }}</div>
</Form.Item>
<Form.Item label="角色" name="role" :rules="[{ required: true, message: '请选择用户角色' }]">
<Select placeholder="请选择" style="width: 100%;" v-model:value="userData.role">
<SelectOption v-for="(it, opIndex) in RoleList" :key="opIndex" :value="it.value">{{ it.label }}
</SelectOption>
</Select>
</Form.Item>
<Form.Item class="flex justify-end mt-10 mb-0">
<Space :size="20">
<Button html-type="submit">确定</Button>
</Space>
</Form.Item>
</Form>
</div>
</template>
<script setup lang="ts">
import { reactive, computed } from 'vue'
import { Form, Input, Button,Select,SelectOption,Space } from "ant-design-vue"
import { RoleList } from '@/core/role';
// import DataField from "@/components/data-fields/index.vue"
// import Button from "../components/button/button.vue";
import { message } from "../components/message";
import Demo from './result/demo.vue'
const userData = reactive<Partial<UserInfo>>({});
const passwordRules = computed(() => {
return userData?.id ? [
{ min: 6, max: 12, message: '请输入6-12位密码' }
] : [
{ required: true, message: '请输入6-12位密码' },
{ min: 6, max: 12, message: '请输入6-12位密码' }
]
})
function handleSave() {
console.log(userData)
if (userData) {
// run(userData)
}
//
}
function handleFinishError(errorInfo: any) {
console.log('handleFinishError', errorInfo)
}
const showMessage = () => {
message.show(
'This notification does not automatically close, and you need to click the close button to close.',
'Notification title',
0
)
}
</script>

View File

@ -1,45 +0,0 @@
<template>
<Form v-model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off"
@finish="onFinish" @finishFailed="onFinishFailed">
<Form.Item label="Username" name="username"
:rules="[{ required: true, message: 'Please input your username!' }]">
<Input v-model:value="formState.username" />
</Form.Item>
<Form.Item label="Password" name="password"
:rules="[{ required: true, message: 'Please input your password!' }]">
<InputPassword v-model:value="formState.password" />
</Form.Item>
<Form.Item name="remember" :wrapper-col="{ offset: 8, span: 16 }">
<Checkbox v-model:checked="formState.remember">Remember me</Checkbox>
</Form.Item>
<Form.Item :wrapper-col="{ offset: 8, span: 16 }">
<Button type="primary" html-type="submit">Submit</Button>
</Form.Item>
</Form>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import { Form, Input, Checkbox, Button, InputPassword } from 'ant-design-vue'
interface FormState {
username: string;
password: string;
remember: boolean;
}
const formState = reactive<FormState>({
username: '',
password: '',
remember: true,
});
const onFinish = (values: any) => {
console.log('Success:', values);
};
const onFinishFailed = (errorInfo: any) => {
console.log('Failed:', errorInfo);
};
</script>

View File

@ -0,0 +1,18 @@
<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";
const showMessage = () => {
message.show(
'This notification does not automatically close, and you need to click the close button to close.',
'Notification title',
0
)
}
</script>

View File

@ -5,10 +5,12 @@ import {
Pagination, Select, SelectOption, Input, Button, Space, Empty, Spin, Pagination, Select, SelectOption, Input, Button, Space, Empty, Spin,
Modal, message Modal, message
} from 'ant-design-vue' } from 'ant-design-vue'
import { RoleEnum, AllRoleList } from '@/core/role'
import { RoleEnum, AllRoleList } from '@/core/enums.ts'
import { columns, getList, deleteUser } from "@/service/api/user"; import { columns, getList, deleteUser } from "@/service/api/user";
import PageHeader from '@/components/page-header.vue' import PageHeader from '@/components/page-header.vue'
import useRequest from "@/service/useRequest"; import useRequest from "@/service/useRequest";
import EditModal from './modal.vue' import EditModal from './modal.vue'
const editUser = ref<Partial<UserInfo>>() const editUser = ref<Partial<UserInfo>>()
@ -18,15 +20,10 @@ const searchParams = ref<UserSearchParam>({
role: '' role: ''
}) })
const { data: allDataList, loading, run, refresh } = useRequest(() => getList(searchParams.value), { const { data: allDataList, loading, run, refresh } = useRequest(() => getList(searchParams.value))
refreshDeps: [searchParams]
})
watch(searchParams, run, { deep: true })
function search() {
searchParams.value.page = 1;
run()
}
function deleteUserById(id: number) { function deleteUserById(id: number) {
deleteUser(id).then(() => { deleteUser(id).then(() => {
message.success('删除成功') message.success('删除成功')
@ -72,10 +69,8 @@ function handleDelete(id: number) {
</Select> </Select>
</Space> </Space>
<Space class="form-item" :size="15"> <Space class="form-item" :size="15">
<Button :icon="h(SearchOutlined)" type="primary" <Button :icon="h(SearchOutlined)" type="primary" @click="run">查询</Button>
@click="search()">查询</Button> <Button :icon="h(PlusOutlined)" class="btn-info" @click="editUser = { id: 0 }">新增</Button>
<Button :icon="h(PlusOutlined)" class="btn-info"
@click="editUser = { id: 0 }">新增</Button>
</Space> </Space>
</Space> </Space>
</div> </div>
@ -111,7 +106,7 @@ function handleDelete(id: number) {
<div v-if="allDataList && allDataList?.total > 0" class="data-page"> <div v-if="allDataList && allDataList?.total > 0" class="data-page">
<Pagination v-model:current="searchParams.page" v-model:page-size="searchParams.limit" <Pagination v-model:current="searchParams.page" v-model:page-size="searchParams.limit"
:total="allDataList.total" :show-total="(total: number) => `共 ${total} 条`" /> :total="allDataList.total" :show-total="(total: number) => `共 ${total} 条`" @change="run" />
</div> </div>
</div> </div>
@ -119,12 +114,5 @@ function handleDelete(id: number) {
</div> </div>
</template> </template>
<style lang="scss"> <style lang="scss">
.ant-btn-dangerous {
background-color: hsl(359, 100%, 60%);
color: white !important;
&:hover {
background-color: hsl(359, 100%, 70%);
}
}
</style> </style>

View File

@ -3,7 +3,7 @@ import { computed, defineModel, reactive } from 'vue';
import { Modal, Form, Input, Select, SelectOption, Space, Button } from 'ant-design-vue'; import { Modal, Form, Input, Select, SelectOption, Space, Button } from 'ant-design-vue';
import { md5 } from 'js-md5'; import { md5 } from 'js-md5';
import { RoleList } from '@/core/role'; import { RoleList } from '@/core/enums';
import useRequest from '@/service/useRequest'; import useRequest from '@/service/useRequest';
import { saveUser } from '@/service/api/user'; import { saveUser } from '@/service/api/user';

View File

@ -29,16 +29,16 @@ export const routes:RouteRecordRaw[] = [
title: '输出计算', title: '输出计算',
icon:'icon-calculator' icon:'icon-calculator'
}, },
component: () => import('./pages/result.vue') component: () => import('./pages/result/index.vue')
}, },
{ {
path: 'data', path: 'product-data',
name: 'data', name: 'product',
meta: { meta: {
title: '数据管理', title: '数据管理',
icon:'icon-input' icon:'icon-input'
}, },
component: () => import('./pages/datas.vue') component: () => import('./pages/product/index.vue')
}, },
{ {
path: 'user', path: 'user',

View File

@ -0,0 +1,28 @@
import { sleep } from "@/core/sleep";
import { post } from "./request";
export const ProductCols = [
{ 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 async function getList(param: ProductSearchParam) {
await sleep(200);
return await post<DataList<ProductInfoModel>>(`/product/all`, param);
}
export function deleteProduct(id: number) {
return post(`/product/delete`, { id })
}
export async function saveProduct(params: Partial<ProductInfoModel>) {
await sleep(200);
return await post(`/product/save`, params);
}

View File

@ -6,6 +6,10 @@ export async function saveUser(params: Partial<UserInfo>) {
await sleep(200); await sleep(200);
return await post<UserInfo>(`/user/save`, params); return await post<UserInfo>(`/user/save`, params);
} }
export async function updateUserPassword(params: ModifyUserPassword) {
await sleep(200);
return await post<UserInfo>(`/user/modify_password`, params);
}
export function userLogin(params: LoginModel) { export function userLogin(params: LoginModel) {
return post<{ token: string; user: UserInfo }>(`/user/login`, params); return post<{ token: string; user: UserInfo }>(`/user/login`, params);

View File

@ -25,6 +25,7 @@ export const fields: BaseModel[] = [
{ id: 8, name: "Ca", alias: "ca" }, { id: 8, name: "Ca", alias: "ca" },
{ id: 9, name: "P", alias: "p" }, { id: 9, name: "P", alias: "p" },
{ id: 10, name: "Mg", alias: "mg" }, { id: 10, name: "Mg", alias: "mg" },
{ id: 10, name: "计量单位", alias: "unit" },
]; ];
export const products: BaseModel[] = [ export const products: BaseModel[] = [
{ id: 1, name: "爱伦多", alias: "ailunduo" }, { id: 1, name: "爱伦多", alias: "ailunduo" },
@ -211,6 +212,7 @@ export function getProductValues() {
product, product,
values: values[product.id], values: values[product.id],
})); }));
console.log(results);
return results; return results;
} }

View File

@ -2,7 +2,7 @@ import { defineStore } from "pinia"
import { onMounted, ref } from "vue" import { onMounted, ref } from "vue"
import { md5 } from "js-md5"; import { md5 } from "js-md5";
import { sleep } from "@/core/sleep.ts"; import { sleep } from "@/core/sleep.ts";
import { userLogin, getInfo } from "./api/user"; import { userLogin, getInfo,saveUser,updateUserPassword } from "./api/user";
import { BizError } from "@/types/core.ts"; import { BizError } from "@/types/core.ts";
@ -37,8 +37,14 @@ export const useUserStore = defineStore('counter', () => {
userInfo.value = undefined; userInfo.value = undefined;
} }
// 更新用户信息 // 更新用户信息
const updateUserInfo = async (info: UserInfo) => { const updateUserInfo = async (info: Partial<UserInfo>) => {
userInfo.value = info await saveUser(info)
await getUserInfo()
}
const updatePassword = async (data: ModifyUserPassword) => {
if(!userInfo.value) throw new BizError('please login',401)
data.id = userInfo.value.id;
await updateUserPassword(data)
} }
// 获取用户信息 // 获取用户信息
const getUserInfo = async () => { const getUserInfo = async () => {
@ -69,6 +75,7 @@ export const useUserStore = defineStore('counter', () => {
login, login,
logout, logout,
updateUserInfo, updateUserInfo,
getUserInfo getUserInfo,
updatePassword
} }
}) })

27
src/types/product.d.ts vendored Normal file
View File

@ -0,0 +1,27 @@
declare type ProductSearchParam = {
page: number;
limit: number;
name?: string;
category?: string;
}
declare type ProductInfoModel = {
id: number;
name: string;
alias: string;
category: string;
unit: string;
power: number;
protein: number;
glu: number;
fat: number;
cellulose: number;
na: number;
k: number;
ca: number;
p: number;
mg: number;
created_at: Date | string;
updated_at: Date | string;
status: number;
}

10
src/types/user.d.ts vendored
View File

@ -1,9 +1,9 @@
declare type LoginModel = { declare interface LoginModel {
account: string; account: string;
password: string; password: string;
} }
declare type UserSearchParam = { declare interface UserSearchParam {
page: number; page: number;
limit: number; limit: number;
nickname?: string; nickname?: string;
@ -21,3 +21,9 @@ declare interface UserInfo {
updated_at: string; updated_at: string;
status: number; status: number;
} }
declare interface ModifyUserPassword {
id?: number;
origin_password: string;
new_password: string;
repeat_password: string;
}