update crud macro

master
veypi 1 year ago
parent 1de22a98d4
commit 2dda72bbf3

@ -35,7 +35,7 @@ CREATE TABLE IF NOT EXISTS `app`
`_key` varchar(32) NOT NULL, `_key` varchar(32) NOT NULL,
`name` varchar(255) NOT NULL, `name` varchar(255) NOT NULL,
`icon` varchar(255), `icon` varchar(255),
`des` MEDIUMTEXT, `des` TEXT,
`user_count` int NOT NULL DEFAULT 0, `user_count` int NOT NULL DEFAULT 0,
`hide` tinyint(1) NOT NULL DEFAULT 0, `hide` tinyint(1) NOT NULL DEFAULT 0,
`join_method` int NOT NULL DEFAULT 0, `join_method` int NOT NULL DEFAULT 0,

@ -6,8 +6,8 @@
// //
use proc_macro2::{Ident, Span}; use proc_macro2::{Ident, Span};
use quote::{quote, ToTokens}; use quote::{format_ident, quote, ToTokens};
use syn::{AttributeArgs, ItemFn, NestedMeta, ReturnType}; use syn::{AttributeArgs, ItemFn, NestedMeta, ReturnType, Token};
pub struct CrudWrap { pub struct CrudWrap {
func: ItemFn, func: ItemFn,
@ -49,24 +49,61 @@ impl ToTokens for CrudWrap {
}; };
} }
}); });
let (args_fields, filter_fields) = match self.args.filters.len() {
1 => {
let pair = &self.args.filters[0];
let k = &pair[0];
let _k = format_ident!("_{}", &pair[0]);
let v = &pair[1];
(
vec![quote! {let #_k = #v; }],
vec![quote! {
filter(crate::models::#model_name::Column::#k.eq(#_k))
}],
)
}
_ => (
self.args
.filters
.iter()
.enumerate()
.map(|(idx, [k, v])| {
let _k = format_ident!("_{}", k);
quote! {
let #_k = #v.#idx;
}
})
.collect(),
self.args
.filters
.iter()
.map(|[k, _]| {
let _k = format_ident!("_{}", k);
quote! {
filter(crate::models::#model_name::Column::#k.eq(#_k))
}
})
.collect(),
),
};
let stream = quote! { let stream = quote! {
#(#fn_attrs)* #(#fn_attrs)*
#func_vis #fn_async fn #fn_name #fn_generics( #func_vis #fn_async fn #fn_name #fn_generics(
#fn_args #fn_args
) -> #fn_output { ) -> #fn_output {
let _id = &id.clone(); let _id = id.clone();
let _data = data.clone(); let _data = data.clone();
let _db = &stat.db().clone(); let _db = &stat.db().clone();
#(#args_fields)*
let f = || async move #func_block; let f = || async move #func_block;
let res = f().await; let res = f().await;
match res { match res {
Err(e) => Err(e), Err(e) => Err(e),
Ok(res) => { Ok(res) => {
let obj = crate::models::#model_name::Entity::find_by_id(_id).one(_db).await?; let obj = crate::models::#model_name::Entity::find().#(#filter_fields).*.one(_db).await?;
let mut obj: crate::models::#model_name::ActiveModel = match obj { let mut obj: crate::models::#model_name::ActiveModel = match obj {
Some(o) => o.into(), Some(o) => o.into(),
None => return Err(Error::NotFound(_id.to_owned())), None => return Err(Error::NotFound("".into())),
}; };
#(#builder_fields)* #(#builder_fields)*
let obj = obj.update(_db).await?; let obj = obj.update(_db).await?;
@ -83,12 +120,14 @@ impl ToTokens for CrudWrap {
struct Crud { struct Crud {
model: syn::Ident, model: syn::Ident,
attrs: Vec<syn::Ident>, attrs: Vec<syn::Ident>,
filters: Vec<[syn::Ident; 2]>,
} }
impl Crud { impl Crud {
fn new(args: AttributeArgs) -> syn::Result<Self> { fn new(args: AttributeArgs) -> syn::Result<Self> {
let mut model: Option<syn::Ident> = None; let mut model: Option<syn::Ident> = None;
let mut attrs: Vec<syn::Ident> = Vec::new(); let mut attrs: Vec<syn::Ident> = Vec::new();
let mut filters: Vec<[syn::Ident; 2]> = Vec::new();
for arg in args { for arg in args {
match arg { match arg {
// NestedMeta::Lit(syn::Lit::Str(lit)) => { // NestedMeta::Lit(syn::Lit::Str(lit)) => {
@ -103,14 +142,36 @@ impl Crud {
} }
None => {} None => {}
}, },
NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
path,
lit: syn::Lit::Str(lit_str),
..
})) => {
match path.get_ident() {
Some(expr) => {
filters.push([expr.to_owned(), format_ident!("{}", lit_str.value())])
}
None => {
return Err(syn::Error::new_spanned(
path,
"Unknown identifier. Available: 'aid='AppId'",
));
}
};
}
_ => { _ => {
return Err(syn::Error::new_spanned(arg, "Unknown attribute.")); return Err(syn::Error::new_spanned(arg, "Unknown attribute."));
} }
} }
} }
println!("|||||||||||____________________");
match model { match model {
Some(model) => Ok(Self { model, attrs }), Some(model) => Ok(Self {
model,
attrs,
filters,
}),
None => Err(syn::Error::new( None => Err(syn::Error::new(
Span::call_site(), Span::call_site(),
"The #[crud(..)] macro requires one `model` name", "The #[crud(..)] macro requires one `model` name",

@ -8,13 +8,12 @@
// //
use actix_web::{web, Responder}; use actix_web::{web, Responder};
use proc::crud_update; use proc::crud_update;
use sea_orm::{ActiveModelTrait, EntityTrait}; use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, TransactionTrait};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{models::app, AppState, Error, Result}; use crate::{models::app, AppState, Error, Result};
// #[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Clone)]
pub struct UpdateOpt { pub struct UpdateOpt {
pub name: Option<String>, pub name: Option<String>,
pub icon: Option<String>, pub icon: Option<String>,
@ -25,7 +24,17 @@ pub struct UpdateOpt {
pub status: Option<i32>, pub status: Option<i32>,
} }
impl UpdateOpt { impl UpdateOpt {
#[crud_update(app, name, icon, des, join_method, role_id, redirect, status)] // #[crud_update(
// app,
// id = "Id",
// name,
// icon,
// des,
// join_method,
// role_id,
// redirect,
// status
// )]
pub async fn update( pub async fn update(
id: web::Path<String>, id: web::Path<String>,
stat: web::Data<AppState>, stat: web::Data<AppState>,

@ -7,7 +7,7 @@
// //
use actix_web::{delete, get, patch, post, web, Responder}; use actix_web::{delete, get, patch, post, web, Responder};
use proc::{access_create, access_delete, access_read, access_update, crud_update}; use proc::{access_create, access_delete, access_read, access_update, crud_update};
use sea_orm::{ActiveModelTrait, EntityTrait, TransactionTrait}; use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, TransactionTrait};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::info; use tracing::info;
@ -93,7 +93,17 @@ pub struct UpdateOpt {
#[patch("/app/{id}")] #[patch("/app/{id}")]
#[access_update("app")] #[access_update("app")]
#[crud_update(app, name, icon, des, join_method, role_id, redirect, status)] #[crud_update(
app,
Id = "_id",
name,
icon,
des,
join_method,
role_id,
redirect,
status
)]
pub async fn update( pub async fn update(
id: web::Path<String>, id: web::Path<String>,
stat: web::Data<AppState>, stat: web::Data<AppState>,

@ -5,9 +5,11 @@
// Distributed under terms of the MIT license. // Distributed under terms of the MIT license.
// //
use actix_web::{get, post, web, Responder}; use actix_web::{get, patch, post, web, Responder};
use proc::access_read; use proc::{access_delete, access_read, crud_update};
use sea_orm::{ColumnTrait, EntityTrait, LoaderTrait, QueryFilter, TransactionTrait}; use sea_orm::{
ActiveModelTrait, ColumnTrait, EntityTrait, LoaderTrait, QueryFilter, TransactionTrait,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -92,3 +94,19 @@ pub async fn add(
db.commit().await?; db.commit().await?;
Ok(web::Json(res)) Ok(web::Json(res))
} }
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpdateOpt {
pub status: Option<i32>,
}
#[patch("/app/{aid}/user/{uid}")]
#[access_delete("app")]
#[crud_update(app_user, AppId = "_id", UserId = "_id", status)]
pub async fn update(
id: web::Path<(String, String)>,
data: web::Json<UpdateOpt>,
stat: web::Data<AppState>,
) -> Result<impl Responder> {
Ok("")
}

@ -29,5 +29,7 @@ pub fn routes(cfg: &mut web::ServiceConfig) {
.service(app::del); .service(app::del);
// cfg.route("/acc", web::get().to(access::UpdateOpt::update)); // cfg.route("/acc", web::get().to(access::UpdateOpt::update));
cfg.service(appuser::get).service(appuser::add); cfg.service(appuser::get)
.service(appuser::add)
.service(appuser::update);
} }

@ -6,6 +6,8 @@
// //
// //
use std::{fs, path::Path};
use actix_multipart::form::{tempfile::TempFile, MultipartForm}; use actix_multipart::form::{tempfile::TempFile, MultipartForm};
use actix_web::{post, web, Responder}; use actix_web::{post, web, Responder};
@ -19,29 +21,45 @@ struct UploadForm {
files: Vec<TempFile>, files: Vec<TempFile>,
} }
#[post("/upload/")] #[post("/upload/{dir:.*}")]
#[access_read("app")] #[access_read("app")]
async fn save_files( async fn save_files(
MultipartForm(form): MultipartForm<UploadForm>, MultipartForm(form): MultipartForm<UploadForm>,
t: web::ReqData<Token>, t: web::ReqData<Token>,
dir: web::Path<String>,
stat: web::Data<AppState>, stat: web::Data<AppState>,
) -> Result<impl Responder> { ) -> Result<impl Responder> {
let l = form.files.len();
let t = t.into_inner(); let t = t.into_inner();
let mut dir = dir.into_inner();
if dir.is_empty() {
dir = "tmp".to_string();
}
let mut res: Vec<String> = Vec::new(); let mut res: Vec<String> = Vec::new();
info!("!|||||||||||_{}_|", l);
form.files.into_iter().for_each(|v| { form.files.into_iter().for_each(|v| {
let fname = v.file_name.unwrap_or("unknown".to_string()); let fname = v.file_name.unwrap_or("unknown".to_string());
let path = format!("{}tmp/{}.{}", stat.media_path, t.id, fname); let root = Path::new(&stat.media_path).join(dir.clone());
info!("saving to {path}"); if !root.exists() {
match v.file.persist(path) { match fs::create_dir_all(root.clone()) {
Ok(_) => {}
Err(e) => {
warn!("{}", e);
}
}
}
let temp_file = format!(
"{}/{}.{}",
root.to_str().unwrap_or(&stat.media_path),
t.id,
fname
);
info!("saving to {temp_file}");
match v.file.persist(temp_file) {
Ok(p) => { Ok(p) => {
info!("{:#?}", p); info!("{:#?}", p);
res.push(format!("/media/tmp/{}.{}", t.id, fname)) res.push(format!("/media/{}/{}.{}", dir, t.id, fname))
} }
Err(e) => { Err(e) => {
warn!("{}", e); warn!("{}", e);
// return Err(Error::InternalServerError);
} }
}; };
}); });

@ -129,8 +129,8 @@ impl AppState {
log_dir: None, log_dir: None,
log_temp_size: None, log_temp_size: None,
log_pack_compress: None, log_pack_compress: None,
media_path: "/Users/veypi/test/media/".to_string(), media_path: "/Users/veypi/test/media".to_string(),
fs_root: "/Users/veypi/test/media/".to_string(), fs_root: "/Users/veypi/test/media".to_string(),
log_level: None, log_level: None,
jwt_secret: None, jwt_secret: None,
_sqlx: None, _sqlx: None,
@ -197,7 +197,7 @@ pub fn init_log() {
tracing_subscriber::fmt() tracing_subscriber::fmt()
.with_line_number(true) .with_line_number(true)
.with_timer(FormatTime {}) .with_timer(FormatTime {})
.with_max_level(Level::DEBUG) .with_max_level(Level::TRACE)
.with_target(false) .with_target(false)
.with_file(true) .with_file(true)
.init(); .init();

@ -90,15 +90,10 @@ async fn web(data: AppState) -> Result<()> {
.unwrap(), .unwrap(),
HeaderValue::from_str(&origin).unwrap(), HeaderValue::from_str(&origin).unwrap(),
); );
headers.insert(
HeaderName::try_from("123").unwrap(),
HeaderValue::from_str("asd").unwrap(),
);
Ok(expr) Ok(expr)
} }
Err(e) => Err(e), Err(e) => Err(e),
}; };
println!("Hi from response");
res res
}) })
}) })

@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize};
pub struct Model { pub struct Model {
#[sea_orm(primary_key)] #[sea_orm(primary_key)]
pub id: i32, pub id: i32,
pub created: Option<DateTime>, pub created: DateTime,
pub updated: Option<DateTime>, pub updated: DateTime,
pub app_id: String, pub app_id: String,
pub name: String, pub name: String,
pub role_id: Option<String>, pub role_id: Option<String>,

@ -10,11 +10,14 @@ use serde::{Deserialize, Serialize};
pub struct Model { pub struct Model {
#[sea_orm(primary_key, auto_increment = false)] #[sea_orm(primary_key, auto_increment = false)]
pub id: String, pub id: String,
pub created: Option<DateTime>, pub created: DateTime,
pub updated: Option<DateTime>, pub updated: DateTime,
#[sea_orm(column_name = "_key")]
#[serde(skip)]
pub key: String, pub key: String,
pub name: String, pub name: String,
pub icon: Option<String>, pub icon: Option<String>,
#[sea_orm(column_type = "Text", nullable)]
pub des: Option<String>, pub des: Option<String>,
pub user_count: i32, pub user_count: i32,
pub hide: i8, pub hide: i8,

@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
)] )]
#[sea_orm(table_name = "app_user")] #[sea_orm(table_name = "app_user")]
pub struct Model { pub struct Model {
pub created: Option<DateTime>, pub created: DateTime,
pub updated: Option<DateTime>, pub updated: DateTime,
#[sea_orm(primary_key, auto_increment = false)] #[sea_orm(primary_key, auto_increment = false)]
pub app_id: String, pub app_id: String,
#[sea_orm(primary_key, auto_increment = false)] #[sea_orm(primary_key, auto_increment = false)]

@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
)] )]
#[sea_orm(table_name = "resource")] #[sea_orm(table_name = "resource")]
pub struct Model { pub struct Model {
pub created: Option<DateTime>, pub created: DateTime,
pub updated: Option<DateTime>, pub updated: DateTime,
#[sea_orm(primary_key, auto_increment = false)] #[sea_orm(primary_key, auto_increment = false)]
pub app_id: String, pub app_id: String,
#[sea_orm(primary_key, auto_increment = false)] #[sea_orm(primary_key, auto_increment = false)]

@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize};
pub struct Model { pub struct Model {
#[sea_orm(primary_key, auto_increment = false)] #[sea_orm(primary_key, auto_increment = false)]
pub id: String, pub id: String,
pub created: Option<DateTime>, pub created: DateTime,
pub updated: Option<DateTime>, pub updated: DateTime,
pub app_id: String, pub app_id: String,
pub name: String, pub name: String,
pub des: Option<String>, pub des: Option<String>,

@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize};
pub struct Model { pub struct Model {
#[sea_orm(primary_key, auto_increment = false)] #[sea_orm(primary_key, auto_increment = false)]
pub id: String, pub id: String,
pub created: Option<DateTime>, pub created: DateTime,
pub updated: Option<DateTime>, pub updated: DateTime,
#[sea_orm(unique)] #[sea_orm(unique)]
pub username: String, pub username: String,
pub nickname: Option<String>, pub nickname: Option<String>,

@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
)] )]
#[sea_orm(table_name = "user_role")] #[sea_orm(table_name = "user_role")]
pub struct Model { pub struct Model {
pub created: Option<DateTime>, pub created: DateTime,
pub updated: Option<DateTime>, pub updated: DateTime,
#[sea_orm(primary_key, auto_increment = false)] #[sea_orm(primary_key, auto_increment = false)]
pub user_id: String, pub user_id: String,
#[sea_orm(primary_key, auto_increment = false)] #[sea_orm(primary_key, auto_increment = false)]

@ -27,8 +27,8 @@ pub async fn init(data: AppState) {
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct UnionAppUser { pub struct UnionAppUser {
pub created: Option<chrono::NaiveDateTime>, pub created: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: chrono::NaiveDateTime,
pub app: Option<app::Model>, pub app: Option<app::Model>,
pub user: Option<user::Model>, pub user: Option<user::Model>,
pub app_id: String, pub app_id: String,

@ -40,7 +40,7 @@ export default {
add(uid: string) { add(uid: string) {
return ajax.post(this.local + uid) return ajax.post(this.local + uid)
}, },
update(uid: number, status: string) { update(uid: string, status: number) {
return ajax.patch(this.local + uid, { status }) return ajax.patch(this.local + uid, { status })
}, },
} }

@ -12,7 +12,7 @@
'']" style="font-size: 24px;" :name="root.type === '']" style="font-size: 24px;" :name="root.type ===
'directory' ? 'v-caret-right' : 'v-file'"> </q-icon> 'directory' ? 'v-caret-right' : 'v-file'"> </q-icon>
<div> <div>
{{ root.filename }} {{ root.basename || '/' }}
</div> </div>
<div class="grow"></div> <div class="grow"></div>
<div>{{ new Date(root.lastmod).toLocaleString() }}</div> <div>{{ new Date(root.lastmod).toLocaleString() }}</div>

@ -64,11 +64,9 @@ const fileUpload = (f: File, cb: (url: string, params: any) => void) => {
* @param params.width 设置宽度可以是像素也可以是百分比图片视频场景下生效 * @param params.width 设置宽度可以是像素也可以是百分比图片视频场景下生效
* @param params.height 设置高度可以是像素也可以是百分比图片视频场景下生效 * @param params.height 设置高度可以是像素也可以是百分比图片视频场景下生效
*/ */
console.log('uploading file' + f.name) oafs.upload([f], props.static_dir).then((e: any) => {
let url = '/abc/' cb(e[0], {
oafs.appdav().upload(url, oafs.rename(f.name), f).then((e: any) => { name: f.name, isBorder: false, isShadow: false, isRadius: false, width: '', height: '',
cb(e, {
name: f.name, isBorder: false, isShadow: false, isRadius: false, width: '80%', height: '80%',
}) })
}) })
} }
@ -77,7 +75,8 @@ const init = () => {
value: props.content, value: props.content,
id: props.eid, id: props.eid,
// isPreviewOnly: props.preview, // isPreviewOnly: props.preview,
callback: {}, callback: {
},
fileUpload: fileUpload, fileUpload: fileUpload,
} as CherryOptions; } as CherryOptions;
config.callback.afterInit = () => { config.callback.afterInit = () => {

@ -16,7 +16,8 @@ let emits = defineEmits<{
}>() }>()
let props = withDefaults(defineProps<{ let props = withDefaults(defineProps<{
multiple?: boolean, multiple?: boolean,
renames?: string renames?: string,
dir?: string,
}>(), { }>(), {
multiple: false, multiple: false,
renames: '' renames: ''
@ -29,7 +30,7 @@ function click() {
const upload = (evt: Event) => { const upload = (evt: Event) => {
evt.preventDefault() evt.preventDefault()
let f = (evt.target as HTMLInputElement).files as FileList let f = (evt.target as HTMLInputElement).files as FileList
oafs.upload(f, props.renames?.split(/[, ]+/)).then((e: any) => { oafs.upload(f, props.dir, props.renames?.split(/[, ]+/)).then((e: any) => {
console.log(e) console.log(e)
emits('success', props.multiple ? e : e[0]) emits('success', props.multiple ? e : e[0])
}) })

@ -51,14 +51,15 @@ const get = (url: string): Promise<string> => {
return fetch(url, { headers: { auth_token: util.getToken() } }).then((response) => response.text()) return fetch(url, { headers: { auth_token: util.getToken() } }).then((response) => response.text())
} }
const upload = (f: FileList | File[], renames?: string[]) => { // rename 可以保持url不变
const upload = (f: FileList | File[], dir?: string, renames?: string[]) => {
return new Promise<string[]>((resolve, reject) => { return new Promise<string[]>((resolve, reject) => {
var data = new FormData(); var data = new FormData();
for (let i = 0; i < f.length; i++) { for (let i = 0; i < f.length; i++) {
let nf = renames ? new File([f[i]], rename(f[i].name, renames[i]), { type: f[i].type }) : f[i] let nf = new File([f[i]], rename(f[i].name, renames && renames[i] ? renames[i] : undefined), { type: f[i].type })
data.append('files', nf, nf.name) data.append('files', nf, nf.name)
} }
axios.post("/api/upload/", data, { axios.post("/api/upload/" + (dir || ''), data, {
headers: { headers: {
"Content-Type": 'multipart/form-data', "Content-Type": 'multipart/form-data',
'auth_token': cfg.token, 'auth_token': cfg.token,

@ -85,8 +85,8 @@ export enum AUStatus {
} }
export interface modelsAppUser { export interface modelsAppUser {
app?: modelsApp, app: modelsApp,
user?: modelsUser, user: modelsUser,
app_id: string app_id: string
user_id: string user_id: string
status: AUStatus status: AUStatus
@ -105,6 +105,7 @@ export interface modelsUser {
status: number status: number
used: number used: number
space: number space: number
au: AUStatus
// Index 前端缓存 // Index 前端缓存
// Index?: number // Index?: number

@ -0,0 +1,15 @@
<!--
* AppAuth.vue
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-10-09 23:18
* Distributed under terms of the MIT license.
-->
<template>
<div></div>
</template>
<script lang="ts" setup>
</script>
<style scoped></style>

@ -44,7 +44,7 @@ const sync = () => {
const save = (des: string) => { const save = (des: string) => {
let a = new File([des], app.value.name + '.md'); let a = new File([des], app.value.name + '.md');
oafs.upload([a]).then(url => { oafs.upload([a], app.value.id).then(url => {
api.app.update(app.value.id, { des: url[0] }).then(e => { api.app.update(app.value.id, { des: url[0] }).then(e => {
edit_mode.value = false edit_mode.value = false
app.value.des = url[0] app.value.des = url[0]

@ -10,17 +10,17 @@
<template v-slot:body-cell-status="props"> <template v-slot:body-cell-status="props">
<q-td :props="props"> <q-td :props="props">
<div> <div>
<q-chip outline :color="statusOpts[props.row.status][1]" text-color="white" icon="event"> <q-chip outline :color="auOpts[props.row.au][1]" text-color="white" icon="event">
{{ statusOpts[props.row.status][0] }} {{ auOpts[props.row.au][0] }}
</q-chip> </q-chip>
</div> </div>
<q-popup-edit v-model="props.row.status" v-slot="scope" buttons <q-popup-edit v-model="props.row.au" v-slot="scope" buttons
@save="update_status(props.row.id, $event, props.row.status)" label-set="确定" label-cancel="取消"> @save="update_status(props.row.id, $event, props.row.au)" label-set="确定" label-cancel="取消">
<div class="mt-4 mb-2">切换状态至</div> <div class="mt-4 mb-2">切换状态至</div>
<div class="q-gutter-sm"> <div class="q-gutter-sm">
<q-radio :key="i" v-for="i in [0, 1, 2, 3]" keep-color v-model="scope.value" :val="i" <q-radio :key="i" v-for="i in [0, 1, 2, 3]" keep-color v-model="scope.value" :val="i" :label="auOpts[i][0]"
:label="statusOpts[i][0]" :color="statusOpts[i][1]" /> :color="auOpts[i][1]" />
</div> </div>
</q-popup-edit> </q-popup-edit>
@ -31,18 +31,18 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { computed, inject, onMounted, Ref, ref, watch } from 'vue';
import { AUStatus, modelsAppUser } from 'src/models'; import { AUStatus, modelsAppUser, modelsUser, modelsApp } from 'src/models';
import { QTableProps } from 'quasar';
import api from 'src/boot/api'; import api from 'src/boot/api';
import { useAppStore } from 'src/stores/app'; import msg from '@veypi/msg';
const statusOpts: { [index: number]: any } = { const auOpts: { [index: number]: any } = {
[AUStatus.OK]: ['正常', 'positive'], [AUStatus.OK]: ['正常', 'positive'],
[AUStatus.Deny]: ['拒绝', 'warning'], [AUStatus.Deny]: ['拒绝', 'warning'],
[AUStatus.Applying]: ['申请中', 'primary'], [AUStatus.Applying]: ['申请中', 'primary'],
[AUStatus.Disabled]: ['禁用', 'warning'], [AUStatus.Disabled]: ['禁用', 'warning'],
} }
let app = inject('app') as Ref<modelsApp>
const columns = [ const columns = [
{ {
name: 'id', name: 'id',
@ -64,23 +64,35 @@ const columns = [
{ name: 'action', field: 'action', align: 'center', label: '操作' }, { name: 'action', field: 'action', align: 'center', label: '操作' },
] as any ] as any
const rows = ref([ const rows = ref([] as modelsUser[])
{
id: '1', username: 'name', nickname: 'asd', created: 'asdsss',
status: 0
}
])
const update_status = (d, s) => { const update_status = (id: string, n: number, old: number) => {
api.app.user(app.value.id).update(id, n).then(e => {
msg.Info('修改成功')
}).catch(e => {
let a = rows.value.find(a => a.id = id) || {} as any
a.status = old
})
console.log([d, s]) console.log([id, n, old])
} }
onMounted(() => { const sync = () => {
api.app.user(useAppStore().id).list('-', { user: true }).then((e: if (!app.value.id) {
return
}
api.app.user(app.value.id).list('-', { user: true }).then((e:
modelsAppUser[]) => { modelsAppUser[]) => {
rows.value = e.map(i => i.user) rows.value = e.map(i => {
i.user.au = i.status
return i.user
})
}) })
}
watch(computed(() => app.value.id), () => sync())
onMounted(() => {
sync()
}) })
</script> </script>

@ -32,7 +32,7 @@
<q-form @submit="create_new"> <q-form @submit="create_new">
<q-input label="应用名" v-model="temp_app.name" :rules="rules.name"></q-input> <q-input label="应用名" v-model="temp_app.name" :rules="rules.name"></q-input>
<div class="flex justify-center my-4 items-center" label='icon'> <div class="flex justify-center my-4 items-center" label='icon'>
<uploader @success="temp_app.icon = $event"> <uploader @success="temp_app.icon = $event" dir="app_icon">
<q-avatar> <q-avatar>
<img :src="temp_app.icon"> <img :src="temp_app.icon">
</q-avatar> </q-avatar>

@ -45,7 +45,7 @@ const routes: RouteRecordRaw[] = [
children: [ children: [
loadcomponents('home', 'app.home', 'AppHome'), loadcomponents('home', 'app.home', 'AppHome'),
loadcomponents('user', 'app.user', 'AppUser'), loadcomponents('user', 'app.user', 'AppUser'),
loadcomponents('user', 'app.user', 'AppUser'), loadcomponents('auth', 'app.auth', 'AppAuth'),
loadcomponents('settings', 'app.settings', 'IndexPage'), loadcomponents('settings', 'app.settings', 'IndexPage'),
] ]
} }

Loading…
Cancel
Save