mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-08 15:51:11 +08:00
【同步】前端项目源码
【修复】工作流兼容问题
This commit is contained in:
145
frontend/apps/allin-ssl/script/create-alias.sh
Normal file
145
frontend/apps/allin-ssl/script/create-alias.sh
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 获取脚本所在目录的绝对路径
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# 项目根目录
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
# src 目录
|
||||
SRC_DIR="$PROJECT_ROOT/src"
|
||||
# tmp 目录
|
||||
TMP_DIR="$PROJECT_ROOT/.temp"
|
||||
|
||||
# 临时文件
|
||||
TEMP_PATHS_FILE="$TMP_DIR/tsconfig_paths.json"
|
||||
TEMP_ALIAS_FILE="$TMP_DIR/vite_alias.js"
|
||||
|
||||
# 清理函数
|
||||
cleanup() {
|
||||
echo "清理临时文件..."
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
|
||||
# 错误处理
|
||||
handle_error() {
|
||||
echo "错误: $1"
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 注册清理函数
|
||||
trap cleanup EXIT
|
||||
|
||||
# 检查并创建 tmp 目录
|
||||
if [ ! -d "$TMP_DIR" ]; then
|
||||
echo "创建临时目录: $TMP_DIR"
|
||||
mkdir -p "$TMP_DIR" || handle_error "无法创建临时目录"
|
||||
fi
|
||||
|
||||
# 初始化临时文件
|
||||
echo "{" > "$TEMP_PATHS_FILE"
|
||||
echo "import path from 'path'" > "$TEMP_ALIAS_FILE"
|
||||
echo "export default {" >> "$TEMP_ALIAS_FILE"
|
||||
|
||||
# 处理 views 目录下的第一层目录
|
||||
if [ -d "$SRC_DIR/views" ]; then
|
||||
echo "处理 views 目录..."
|
||||
# 确保没有尾随逗号的最后一个条目
|
||||
view_dirs=()
|
||||
while IFS= read -r dir; do
|
||||
if [ -d "$dir" ]; then
|
||||
dir_name=$(basename "$dir")
|
||||
view_dirs+=("$dir_name")
|
||||
fi
|
||||
done < <(find "$SRC_DIR/views" -mindepth 1 -maxdepth 1 -type d)
|
||||
|
||||
# 处理 views 子目录
|
||||
total=${#view_dirs[@]}
|
||||
for ((i=0; i<total; i++)); do
|
||||
dir_name=${view_dirs[$i]}
|
||||
echo " \"@$dir_name/*\": [\"./src/views/$dir_name/*\"]" >> "$TEMP_PATHS_FILE"
|
||||
echo " '@$dir_name': path.resolve(__dirname, 'src/views/$dir_name')," >> "$TEMP_ALIAS_FILE"
|
||||
# 如果不是最后一个元素,添加逗号
|
||||
if [ $i -lt $((total-1)) ]; then
|
||||
echo "," >> "$TEMP_PATHS_FILE"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 处理 src 目录下的所有目录
|
||||
echo "处理 src 目录下的其他目录..."
|
||||
src_dirs=()
|
||||
while IFS= read -r dir; do
|
||||
if [ -d "$dir" ] && [ "$(basename "$dir")" != "views" ]; then
|
||||
dir_name=$(basename "$dir")
|
||||
src_dirs+=("$dir_name")
|
||||
fi
|
||||
done < <(find "$SRC_DIR" -mindepth 1 -maxdepth 1 -type d)
|
||||
|
||||
# 如果之前有 views 目录的条目,添加逗号
|
||||
if [ ${#view_dirs[@]} -gt 0 ] && [ ${#src_dirs[@]} -gt 0 ]; then
|
||||
echo "," >> "$TEMP_PATHS_FILE"
|
||||
fi
|
||||
|
||||
# 处理其他目录
|
||||
total=${#src_dirs[@]}
|
||||
for ((i=0; i<total; i++)); do
|
||||
dir_name=${src_dirs[$i]}
|
||||
echo " \"@$dir_name/*\": [\"./src/$dir_name/*\"]" >> "$TEMP_PATHS_FILE"
|
||||
echo " '@$dir_name': path.resolve(__dirname, 'src/$dir_name')," >> "$TEMP_ALIAS_FILE"
|
||||
# 如果不是最后一个元素,添加逗号
|
||||
if [ $i -lt $((total-1)) ]; then
|
||||
echo "," >> "$TEMP_PATHS_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
# 添加根路径(确保添加逗号如果之前有其他条目)
|
||||
if [ ${#view_dirs[@]} -gt 0 ] || [ ${#src_dirs[@]} -gt 0 ]; then
|
||||
echo "," >> "$TEMP_PATHS_FILE"
|
||||
fi
|
||||
echo " \"@/*\": [\"./src/*\"]" >> "$TEMP_PATHS_FILE"
|
||||
echo "}" >> "$TEMP_PATHS_FILE"
|
||||
|
||||
# 添加根路径到 alias 配置
|
||||
echo " '@': path.resolve(__dirname, 'src')" >> "$TEMP_ALIAS_FILE"
|
||||
echo "}" >> "$TEMP_ALIAS_FILE"
|
||||
|
||||
# 更新 tsconfig.app.json
|
||||
echo "更新 tsconfig.app.json..."
|
||||
TSCONFIG="$PROJECT_ROOT/tsconfig.app.json"
|
||||
if [ -f "$TSCONFIG" ]; then
|
||||
# 创建临时文件
|
||||
TSCONFIG_TMP="${TSCONFIG}.tmp"
|
||||
|
||||
# 使用 jq 处理 JSON(如果可用)
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
jq --arg paths "$(cat "$TEMP_PATHS_FILE")" '.compilerOptions.paths = $paths' "$TSCONFIG" > "$TSCONFIG_TMP" \
|
||||
&& mv "$TSCONFIG_TMP" "$TSCONFIG" \
|
||||
|| handle_error "更新 tsconfig.app.json 失败"
|
||||
else
|
||||
# 回退到 sed 方案
|
||||
sed -e '/"paths":/,/}/c\ "paths": '"$(cat "$TEMP_PATHS_FILE")"',' "$TSCONFIG" > "$TSCONFIG_TMP" \
|
||||
&& mv "$TSCONFIG_TMP" "$TSCONFIG" \
|
||||
|| handle_error "更新 tsconfig.app.json 失败"
|
||||
fi
|
||||
echo "tsconfig.app.json 更新成功"
|
||||
else
|
||||
handle_error "找不到 tsconfig.app.json 文件"
|
||||
fi
|
||||
|
||||
# 更新 vite.config.ts
|
||||
echo "更新 vite.config.ts..."
|
||||
VITE_CONFIG="$PROJECT_ROOT/vite.config.ts"
|
||||
if [ -f "$VITE_CONFIG" ]; then
|
||||
VITE_CONFIG_TMP="${VITE_CONFIG}.tmp"
|
||||
|
||||
# 使用 sed 更新 alias 配置
|
||||
sed -e '/resolve: {/,/}/c\ resolve: {\n alias: '"$(cat "$TEMP_ALIAS_FILE")"'\n },' "$VITE_CONFIG" > "$VITE_CONFIG_TMP" \
|
||||
&& mv "$VITE_CONFIG_TMP" "$VITE_CONFIG" \
|
||||
|| handle_error "更新 vite.config.ts 失败"
|
||||
|
||||
echo "vite.config.ts 更新成功"
|
||||
else
|
||||
handle_error "找不到 vite.config.ts 文件"
|
||||
fi
|
||||
|
||||
echo "路径别名配置更新完成!"
|
||||
891
frontend/apps/allin-ssl/script/create-roles.sh
Normal file
891
frontend/apps/allin-ssl/script/create-roles.sh
Normal file
@@ -0,0 +1,891 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 遇到错误时退出
|
||||
set -e
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "使用方法: ./create-roles.sh [选项]"
|
||||
echo "选项:"
|
||||
echo " -h, --help 显示帮助信息"
|
||||
echo
|
||||
echo "此脚本在 src/views 目录下创建角色管理相关的 Vue3 TSX 路由视图结构"
|
||||
echo "将生成以下结构:"
|
||||
echo "src/views/<角色名称>"
|
||||
echo "├── index.tsx # 入口文件"
|
||||
echo "├── useController.ts # 控制器"
|
||||
echo "├── useStore.ts # 状态管理"
|
||||
echo "├── index.module.css # 样式文件"
|
||||
echo "├── types.d.ts # 类型定义"
|
||||
echo "├── children/ # 子路由"
|
||||
echo "│ └── permissions # 权限管理子路由"
|
||||
echo "│ ├── index.tsx # 视图"
|
||||
echo "│ ├── index.module.css # 样式"
|
||||
echo "│ ├── useController.ts # 控制器"
|
||||
echo "│ ├── useStore.ts # 状态管理"
|
||||
echo "│ └── types.d.ts # 类型定义"
|
||||
echo "└── components/ # 组件"
|
||||
echo " └── role-form # 角色表单组件"
|
||||
echo " ├── index.tsx # 视图"
|
||||
echo " ├── index.module.css # 样式"
|
||||
echo " ├── useController.ts # 控制器"
|
||||
echo " ├── useStore.ts # 状态管理"
|
||||
echo " └── types.d.ts # 类型定义"
|
||||
echo
|
||||
echo "同时会创建:"
|
||||
echo "src/api/<角色名称>.ts # API 文件"
|
||||
echo "src/types/<角色名称>.d.ts # 类型定义文件"
|
||||
}
|
||||
|
||||
# 解析命令行参数
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "错误: 未知参数 $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 交互式选择函数
|
||||
select_option() {
|
||||
local prompt="$1"
|
||||
local options=("是" "否")
|
||||
local selected
|
||||
|
||||
echo "$prompt"
|
||||
select choice in "${options[@]}"; do
|
||||
case $REPLY in
|
||||
1|2)
|
||||
selected=$choice
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "请选择有效的选项 [1-2]"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ "$selected" == "是" ]] && return 0 || return 1
|
||||
}
|
||||
|
||||
# 交互式输入路由名称
|
||||
read -p "请输入路由名称 (routerName): " ROUTER_NAME
|
||||
if [ -z "$ROUTER_NAME" ]; then
|
||||
echo "错误: 路由名称不能为空"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 询问是否创建子路由
|
||||
if select_option "是否创建子路由?"; then
|
||||
read -p "请输入子路由名称 [默认: list]: " CHILD_ROUTER_NAME
|
||||
CHILD_ROUTER_NAME=${CHILD_ROUTER_NAME:-"list"}
|
||||
echo "将创建子路由: $CHILD_ROUTER_NAME"
|
||||
else
|
||||
CHILD_ROUTER_NAME=""
|
||||
echo "不创建子路由"
|
||||
fi
|
||||
|
||||
# 询问是否创建组件
|
||||
if select_option "是否创建组件?"; then
|
||||
read -p "请输入组件名称 [默认: todo-form]: " COMPONENT_NAME
|
||||
COMPONENT_NAME=${COMPONENT_NAME:-"todo-form"}
|
||||
echo "将创建组件: $COMPONENT_NAME"
|
||||
else
|
||||
COMPONENT_NAME=""
|
||||
echo "不创建组件"
|
||||
fi
|
||||
|
||||
# 获取脚本所在目录的绝对路径
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
# 获取项目根目录
|
||||
PROJECT_ROOT="$SCRIPT_DIR/../"
|
||||
|
||||
# 创建目录函数
|
||||
create_dir() {
|
||||
local dir="$1"
|
||||
mkdir -p "$PROJECT_ROOT/$dir"
|
||||
}
|
||||
|
||||
# 确保必需的目录存在
|
||||
create_dir "src/views"
|
||||
create_dir "src/api"
|
||||
create_dir "src/types"
|
||||
|
||||
# 创建主目录结构
|
||||
create_dir "src/views/$ROUTER_NAME/children/$CHILD_ROUTER_NAME"
|
||||
create_dir "src/views/$ROUTER_NAME/components/$COMPONENT_NAME"
|
||||
|
||||
# 创建 API 文件
|
||||
cat > "$PROJECT_ROOT/src/api/${ROUTER_NAME}.ts" << ''
|
||||
|
||||
# 创建类型定义文件
|
||||
cat > "$PROJECT_ROOT/src/types/${ROUTER_NAME}.d.ts" << ''
|
||||
|
||||
# 创建主路由类型文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/types.d.ts" << EOL
|
||||
export interface Todo {
|
||||
id: string
|
||||
title: string
|
||||
completed: boolean
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface TodoState {
|
||||
todos: Todo[]
|
||||
loading: boolean
|
||||
error: string | null
|
||||
}
|
||||
EOL
|
||||
|
||||
# 创建主路由状态管理文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/useStore.ts" << EOL
|
||||
import { defineStore } from '@baota/pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { Todo, TodoState } from './types'
|
||||
|
||||
const store = defineStore('todo-store', () => {
|
||||
const todos = ref<Todo[]>([])
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
const addTodo = (title: string) => {
|
||||
const newTodo: Todo = {
|
||||
id: Date.now().toString(),
|
||||
title,
|
||||
completed: false,
|
||||
createdAt: new Date().toISOString()
|
||||
}
|
||||
todos.value.push(newTodo)
|
||||
}
|
||||
|
||||
const toggleTodo = (id: string) => {
|
||||
const todo = todos.value.find(t => t.id === id)
|
||||
if (todo) {
|
||||
todo.completed = !todo.completed
|
||||
}
|
||||
}
|
||||
|
||||
const removeTodo = (id: string) => {
|
||||
todos.value = todos.value.filter(t => t.id !== id)
|
||||
}
|
||||
|
||||
return {
|
||||
todos,
|
||||
loading,
|
||||
error,
|
||||
addTodo,
|
||||
toggleTodo,
|
||||
removeTodo
|
||||
}
|
||||
})
|
||||
|
||||
export const useStore = () => store()
|
||||
EOL
|
||||
|
||||
# 创建主路由控制器文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/useController.ts" << EOL
|
||||
import { onMounted } from 'vue'
|
||||
import { storeToRefs } from '@baota/pinia'
|
||||
import { useStore } from './useStore'
|
||||
|
||||
export const useController = () => {
|
||||
const store = useStore()
|
||||
const { todos, loading, error } = storeToRefs(store)
|
||||
|
||||
const handleAddTodo = (title: string) => {
|
||||
if (title.trim()) {
|
||||
store.addTodo(title.trim())
|
||||
}
|
||||
}
|
||||
|
||||
const handleToggleTodo = (id: string) => {
|
||||
store.toggleTodo(id)
|
||||
}
|
||||
|
||||
const handleRemoveTodo = (id: string) => {
|
||||
store.removeTodo(id)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 可以在这里加载初始数据
|
||||
console.log('Todo List Component Mounted')
|
||||
})
|
||||
|
||||
return {
|
||||
todos,
|
||||
loading,
|
||||
error,
|
||||
handleAddTodo,
|
||||
handleToggleTodo,
|
||||
handleRemoveTodo
|
||||
}
|
||||
}
|
||||
EOL
|
||||
|
||||
# 创建主路由样式文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/index.module.css" << EOL
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 32px;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 8px 16px;
|
||||
background: #42b883;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: #3aa876;
|
||||
}
|
||||
|
||||
.todoList {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.todoItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.todoCheckbox {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.todoTitle {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.todoTitle.completed {
|
||||
text-decoration: line-through;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.deleteButton {
|
||||
padding: 4px 8px;
|
||||
background: #ff4757;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.deleteButton:hover {
|
||||
background: #ff3748;
|
||||
}
|
||||
EOL
|
||||
|
||||
# 创建主路由入口文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/index.tsx" << EOL
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import { useController } from './useController'
|
||||
import styles from './index.module.css'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TodoList',
|
||||
|
||||
setup() {
|
||||
const { todos, handleAddTodo, handleToggleTodo, handleRemoveTodo } = useController()
|
||||
const newTodo = ref('')
|
||||
|
||||
const onSubmit = (e: Event) => {
|
||||
e.preventDefault()
|
||||
handleAddTodo(newTodo.value)
|
||||
newTodo.value = ''
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div class={styles.container}>
|
||||
<header class={styles.header}>
|
||||
<h1 class={styles.title}>Todo List</h1>
|
||||
</header>
|
||||
|
||||
<form class={styles.form} onSubmit={onSubmit}>
|
||||
<input
|
||||
class={styles.input}
|
||||
type="text"
|
||||
v-model={newTodo.value}
|
||||
placeholder="添加新任务..."
|
||||
/>
|
||||
<button class={styles.button} type="submit">
|
||||
添加
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<ul class={styles.todoList}>
|
||||
{todos.value.map(todo => (
|
||||
<li key={todo.id} class={styles.todoItem}>
|
||||
<input
|
||||
type="checkbox"
|
||||
class={styles.todoCheckbox}
|
||||
checked={todo.completed}
|
||||
onChange={() => handleToggleTodo(todo.id)}
|
||||
/>
|
||||
<span class={[
|
||||
styles.todoTitle,
|
||||
todo.completed && styles.completed
|
||||
]}>
|
||||
{todo.title}
|
||||
</span>
|
||||
<button
|
||||
class={styles.deleteButton}
|
||||
onClick={() => handleRemoveTodo(todo.id)}
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
EOL
|
||||
|
||||
# 在脚本开头添加大写转换函数
|
||||
to_upper_first() {
|
||||
local str="$1"
|
||||
local first_char=$(echo "${str:0:1}" | tr '[:lower:]' '[:upper:]')
|
||||
echo "$first_char${str:1}"
|
||||
}
|
||||
|
||||
# 存储转换后的变量
|
||||
ROUTER_NAME_PASCAL=$(to_upper_first "$ROUTER_NAME")
|
||||
|
||||
# 创建表单组件类型文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/components/$COMPONENT_NAME/types.d.ts" << EOL
|
||||
import type { ${ROUTER_NAME_PASCAL}Data } from '@/types/${ROUTER_NAME}'
|
||||
|
||||
export interface FormProps {
|
||||
data?: ${ROUTER_NAME_PASCAL}Data | null
|
||||
}
|
||||
|
||||
export interface FormEmits {
|
||||
(e: 'submit', data: ${ROUTER_NAME_PASCAL}Data): void
|
||||
(e: 'cancel'): void
|
||||
}
|
||||
EOL
|
||||
|
||||
# 创建表单组件状态管理文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/components/$COMPONENT_NAME/useStore.ts" << EOL
|
||||
import { defineStore } from '@baota/pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { ${ROUTER_NAME_PASCAL}Data } from '@/types/${ROUTER_NAME}'
|
||||
|
||||
// 定义 store
|
||||
const store = defineStore('${ROUTER_NAME}-form-store', () => {
|
||||
const loading = ref(false)
|
||||
const formData = ref<${ROUTER_NAME_PASCAL}Data>({
|
||||
id: '',
|
||||
name: '',
|
||||
code: '',
|
||||
description: '',
|
||||
permissions: [],
|
||||
createdAt: '',
|
||||
updatedAt: ''
|
||||
})
|
||||
|
||||
return {
|
||||
loading,
|
||||
formData
|
||||
}
|
||||
})
|
||||
|
||||
export const useStore = () => store()
|
||||
EOL
|
||||
|
||||
# 创建表单组件控制器文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/components/$COMPONENT_NAME/useController.ts" << EOL
|
||||
import { onMounted } from 'vue'
|
||||
import { storeToRefs } from '@baota/pinia'
|
||||
import { useStore } from './useStore'
|
||||
import type { ${ROUTER_NAME_PASCAL}Data } from '@/types/${ROUTER_NAME}'
|
||||
|
||||
export const useController = (initialData?: ${ROUTER_NAME_PASCAL}Data | null) => {
|
||||
const store = useStore()
|
||||
const storeRef = storeToRefs(store)
|
||||
|
||||
onMounted(() => {
|
||||
if (initialData) {
|
||||
store.formData = { ...initialData }
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
...storeRef
|
||||
}
|
||||
}
|
||||
EOL
|
||||
|
||||
# 创建表单组件样式文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/components/$COMPONENT_NAME/index.module.css" << EOL
|
||||
.form {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.formTitle {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.formItem {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.input:hover {
|
||||
border-color: #40a9ff;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
border-color: #1890ff;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
|
||||
.textarea {
|
||||
composes: input;
|
||||
min-height: 100px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.primaryButton {
|
||||
composes: button;
|
||||
background: #1890ff;
|
||||
color: #fff;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.primaryButton:hover {
|
||||
background: #40a9ff;
|
||||
}
|
||||
EOL
|
||||
|
||||
# 创建表单组件入口文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/components/$COMPONENT_NAME/index.tsx" << EOL
|
||||
import { defineComponent } from 'vue'
|
||||
import { useController } from './useController'
|
||||
import type { FormProps, FormEmits } from './types'
|
||||
import styles from './index.module.css'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RoleForm',
|
||||
|
||||
props: {
|
||||
data: {
|
||||
type: Object as PropType<FormProps['data']>,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
|
||||
emits: ['submit', 'cancel'],
|
||||
|
||||
setup(props, { emit }) {
|
||||
const { formData } = useController(props.data)
|
||||
|
||||
const handleSubmit = (e: Event) => {
|
||||
e.preventDefault()
|
||||
emit('submit', formData.value)
|
||||
}
|
||||
|
||||
return () => (
|
||||
<form class={styles.form} onSubmit={handleSubmit}>
|
||||
<h3 class={styles.formTitle}>
|
||||
{props.data ? '编辑角色' : '创建角色'}
|
||||
</h3>
|
||||
|
||||
<div class={styles.formItem}>
|
||||
<label class={styles.label}>角色名称</label>
|
||||
<input
|
||||
class={styles.input}
|
||||
type="text"
|
||||
v-model={formData.value.name}
|
||||
placeholder="请输入角色名称"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class={styles.formItem}>
|
||||
<label class={styles.label}>角色代码</label>
|
||||
<input
|
||||
class={styles.input}
|
||||
type="text"
|
||||
v-model={formData.value.code}
|
||||
placeholder="请输入角色代码"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class={styles.formItem}>
|
||||
<label class={styles.label}>描述</label>
|
||||
<textarea
|
||||
class={styles.textarea}
|
||||
v-model={formData.value.description}
|
||||
placeholder="请输入角色描述"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class={styles.actions}>
|
||||
<button
|
||||
type="button"
|
||||
class={styles.button}
|
||||
onClick={() => emit('cancel')}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button type="submit" class={styles.primaryButton}>
|
||||
确定
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
})
|
||||
EOL
|
||||
|
||||
# 创建子路由类型文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/children/$CHILD_ROUTER_NAME/types.d.ts" << EOL
|
||||
import type { ${ROUTER_NAME_PASCAL}Data } from '@/types/${ROUTER_NAME}'
|
||||
|
||||
export interface Permission {
|
||||
code: string
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export interface PermissionState {
|
||||
loading: boolean
|
||||
data: ${ROUTER_NAME_PASCAL}Data | null
|
||||
permissions: Permission[]
|
||||
selectedPermissions: string[]
|
||||
}
|
||||
EOL
|
||||
|
||||
# 创建子路由状态管理文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/children/$CHILD_ROUTER_NAME/useStore.ts" << EOL
|
||||
import { defineStore } from '@baota/pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { ${ROUTER_NAME_PASCAL}Data } from '@/types/${ROUTER_NAME}'
|
||||
import type { Permission } from './types'
|
||||
|
||||
// 定义 store
|
||||
const store = defineStore('${ROUTER_NAME}-permissions-store', () => {
|
||||
const loading = ref(false)
|
||||
const data = ref<${ROUTER_NAME_PASCAL}Data | null>(null)
|
||||
const permissions = ref<Permission[]>([])
|
||||
const selectedPermissions = ref<string[]>([])
|
||||
|
||||
return {
|
||||
loading,
|
||||
data,
|
||||
permissions,
|
||||
selectedPermissions
|
||||
}
|
||||
})
|
||||
|
||||
// 导出 store
|
||||
export const useStore = () => store()
|
||||
EOL
|
||||
|
||||
# 创建子路由控制器文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/children/$CHILD_ROUTER_NAME/useController.ts" << EOL
|
||||
import { onMounted } from 'vue'
|
||||
import { storeToRefs } from '@baota/pinia'
|
||||
import { useStore } from './useStore'
|
||||
import { get${ROUTER_NAME_PASCAL}Data } from '@/api/${ROUTER_NAME}'
|
||||
|
||||
export const useController = (roleId: string) => {
|
||||
const store = useStore()
|
||||
const storeRef = storeToRefs(store)
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
store.loading = true
|
||||
const data = await get${ROUTER_NAME_PASCAL}Data(roleId)
|
||||
store.data = data
|
||||
store.selectedPermissions = data.permissions
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error)
|
||||
} finally {
|
||||
store.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
store.loading = true
|
||||
// 调用保存API
|
||||
store.loading = false
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
return {
|
||||
...storeRef,
|
||||
handleSave
|
||||
}
|
||||
}
|
||||
EOL
|
||||
|
||||
# 创建子路由样式文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/children/$CHILD_ROUTER_NAME/index.module.css" << EOL
|
||||
.container {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.content {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.permissionList {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 16px;
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.permissionItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.primaryButton {
|
||||
composes: button;
|
||||
background: #1890ff;
|
||||
color: #fff;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.primaryButton:hover {
|
||||
background: #40a9ff;
|
||||
}
|
||||
EOL
|
||||
|
||||
# 创建子路由入口文件
|
||||
cat > "$PROJECT_ROOT/src/views/$ROUTER_NAME/children/$CHILD_ROUTER_NAME/index.tsx" << EOL
|
||||
import { defineComponent } from 'vue'
|
||||
import { useRoute, useRouter } from '@baota/router'
|
||||
import { useController } from './useController'
|
||||
import styles from './index.module.css'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RolePermissions',
|
||||
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const roleId = route.params.id as string
|
||||
|
||||
const {
|
||||
loading,
|
||||
data,
|
||||
permissions,
|
||||
selectedPermissions,
|
||||
handleSave
|
||||
} = useController(roleId)
|
||||
|
||||
const handleCancel = () => {
|
||||
router.push('/${ROUTER_NAME}')
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div class={styles.container}>
|
||||
<div class={styles.header}>
|
||||
<h1 class={styles.title}>权限设置</h1>
|
||||
</div>
|
||||
|
||||
<div class={styles.content}>
|
||||
{loading.value ? (
|
||||
<div class={styles.loading}>加载中...</div>
|
||||
) : (
|
||||
<>
|
||||
<h2>{data.value?.name} - 权限配置</h2>
|
||||
|
||||
<div class={styles.permissionList}>
|
||||
{permissions.value.map(permission => (
|
||||
<label
|
||||
key={permission.code}
|
||||
class={styles.permissionItem}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
value={permission.code}
|
||||
v-model={selectedPermissions.value}
|
||||
/>
|
||||
<span>{permission.name}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div class={styles.actions}>
|
||||
<button
|
||||
class={styles.button}
|
||||
onClick={handleCancel}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
class={styles.primaryButton}
|
||||
onClick={handleSave}
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
EOL
|
||||
|
||||
echo "✨ 文件结构生成成功!"
|
||||
echo "📁 主路由: $PROJECT_ROOT/src/views/$ROUTER_NAME"
|
||||
echo "📁 子路由: $PROJECT_ROOT/src/views/$ROUTER_NAME/children/$CHILD_ROUTER_NAME"
|
||||
echo "📁 组件: $PROJECT_ROOT/src/views/$ROUTER_NAME/components/$COMPONENT_NAME"
|
||||
echo "📄 API文件: $PROJECT_ROOT/src/api/${ROUTER_NAME}.ts"
|
||||
echo "📄 类型文件: $PROJECT_ROOT/src/types/${ROUTER_NAME}.d.ts"
|
||||
echo
|
||||
echo "目录结构:"
|
||||
echo "├── src/views/$ROUTER_NAME"
|
||||
echo "│ ├── index.tsx"
|
||||
echo "│ ├── useController.ts"
|
||||
echo "│ ├── useStore.ts"
|
||||
echo "│ ├── index.module.css"
|
||||
echo "│ ├── types.d.ts"
|
||||
echo "│ ├── children"
|
||||
echo "│ │ └── $CHILD_ROUTER_NAME"
|
||||
echo "│ │ ├── index.tsx"
|
||||
echo "│ │ ├── index.module.css"
|
||||
echo "│ │ ├── useController.ts"
|
||||
echo "│ │ ├── useStore.ts"
|
||||
echo "│ │ └── types.d.ts"
|
||||
echo "│ └── components"
|
||||
echo "│ └── $COMPONENT_NAME"
|
||||
echo "│ ├── index.tsx"
|
||||
echo "│ ├── index.module.css"
|
||||
echo "│ ├── useController.ts"
|
||||
echo "│ ├── useStore.ts"
|
||||
echo "│ └── types.d.ts"
|
||||
echo "├── src/api"
|
||||
echo "│ └── ${ROUTER_NAME}.ts"
|
||||
echo "└── src/types"
|
||||
echo " └── ${ROUTER_NAME}.d.ts"
|
||||
Reference in New Issue
Block a user