feat: Update .gitignore and enhance user management features

- Added new entries to .gitignore to exclude Vite cache, environment variable templates, and sensitive files.
- Modified user management routes to allow access for both super admins and store staff, enabling store staff to add users for their respective stores.
- Refactored dashboard navigation to include a function for routing with store context.
- Enhanced user form to display current store information for non-super admin users.
This commit is contained in:
ethanfly 2026-02-07 02:19:04 +08:00
parent 8f9eb38666
commit 19784bddb9
5 changed files with 80 additions and 19 deletions

35
.gitignore vendored
View File

@ -3,20 +3,27 @@
# ============================================ # ============================================
node_modules/ node_modules/
**/node_modules/ **/node_modules/
# 子目录下 node_modules 内的缓存/预构建(避免被跟踪)
**/node_modules/.vite/
**/node_modules/.cache/
# ============================================ # ============================================
# 环境变量(含敏感信息,勿提交) # 环境变量(含敏感信息,勿提交)
# ============================================ # ============================================
.env .env
.env.*
.env.local .env.local
.env.*.local .env.*.local
.env.production .env.production
.env.development .env.development
# 各子项目的环境变量 .env.test
# 各子项目的环境变量(保留 env-template.txt 作为模板)
server/.env server/.env
admin/.env.local admin/.env.local
admin/.env admin/.env
miniprogram/.env miniprogram/.env
# 不忽略环境变量模板文件
!**/env-template.txt
# ============================================ # ============================================
# 构建产物 # 构建产物
@ -45,10 +52,12 @@ lerna-debug.log*
# ============================================ # ============================================
.cache/ .cache/
.vite/ .vite/
**/.vite/
.parcel-cache/ .parcel-cache/
.eslintcache .eslintcache
.prettiercache
*.local *.local
# Vite 缓存 # Vite 预构建与缓存
admin/.vite/ admin/.vite/
admin/node_modules/.vite/ admin/node_modules/.vite/
@ -66,6 +75,8 @@ uploads/*
# ============================================ # ============================================
.idea/ .idea/
.vscode/ .vscode/
!.vscode/extensions.json
!.vscode/settings.json.example
*.suo *.suo
*.ntvs* *.ntvs*
*.njsproj *.njsproj
@ -77,6 +88,8 @@ uploads/*
.project .project
.classpath .classpath
.settings/ .settings/
# Cursor
.cursor/
# ============================================ # ============================================
# 系统文件 # 系统文件
@ -104,11 +117,13 @@ playwright-report/
# ============================================ # ============================================
# 微信小程序 # 微信小程序
# ============================================ # ============================================
# 微信开发者工具本地配置(含工具偏好设置 # 微信开发者工具本地配置(含工具偏好、AppID 等,勿提交
miniprogram/project.private.config.json miniprogram/project.private.config.json
# 小程序编译产物(如果有) # 小程序 npm 构建产物
miniprogram/miniprogram_npm/ miniprogram/miniprogram_npm/
miniprogram/node_modules/ miniprogram/node_modules/
# 小程序本地密钥/私密配置
miniprogram/private/
# ============================================ # ============================================
# 临时文件 # 临时文件
@ -128,6 +143,16 @@ temp/
*.sqlite *.sqlite
*.sqlite3 *.sqlite3
# ============================================
# 密钥与证书
# ============================================
*.pem
*.key
*.crt
*.p12
*.pfx
secrets/
# ============================================ # ============================================
# 其他 # 其他
# ============================================ # ============================================
@ -135,3 +160,5 @@ temp/
# package-lock.json # package-lock.json
# yarn.lock # yarn.lock
# pnpm-lock.yaml # pnpm-lock.yaml
# 对话/代理生成目录(若存在)
agent-transcripts/

View File

@ -23,7 +23,7 @@ const routes = [
path: "users", path: "users",
name: "Users", name: "Users",
component: () => import("@/views/user/index.vue"), component: () => import("@/views/user/index.vue"),
meta: { title: "用户管理", icon: "User", superAdmin: true }, meta: { title: "用户管理", icon: "User" },
}, },
{ {
path: "stores", path: "stores",

View File

@ -51,15 +51,15 @@
<el-icon class="icon"><View /></el-icon> <el-icon class="icon"><View /></el-icon>
<span class="label">扫码核销</span> <span class="label">扫码核销</span>
</div> </div>
<div class="quick-action-btn" @click="$router.push('/display/ranking')"> <div class="quick-action-btn" @click="goToDisplay('/display/ranking')">
<el-icon class="icon"><Monitor /></el-icon> <el-icon class="icon"><Monitor /></el-icon>
<span class="label">天梯大屏()</span> <span class="label">天梯大屏()</span>
</div> </div>
<div class="quick-action-btn" @click="$router.push('/display/ranking-orange')"> <div class="quick-action-btn" @click="goToDisplay('/display/ranking-orange')">
<el-icon class="icon"><Monitor /></el-icon> <el-icon class="icon"><Monitor /></el-icon>
<span class="label">天梯大屏()</span> <span class="label">天梯大屏()</span>
</div> </div>
<div class="quick-action-btn" @click="$router.push('/display/ladder-summary')"> <div class="quick-action-btn" @click="goToDisplay('/display/ladder-summary')">
<el-icon class="icon"><Monitor /></el-icon> <el-icon class="icon"><Monitor /></el-icon>
<span class="label">天梯汇总大屏</span> <span class="label">天梯汇总大屏</span>
</div> </div>
@ -273,12 +273,29 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, nextTick, watch } from 'vue' import { ref, onMounted, nextTick, watch, computed } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { View, Loading, InfoFilled, Search, User, Check, Monitor } from '@element-plus/icons-vue' import { View, Loading, InfoFilled, Search, User, Check, Monitor } from '@element-plus/icons-vue'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useUserStore } from '@/stores/user'
import { getDashboard, getPointActions, executePointAction, getMatches, getPointOrders, verifyOrder, verifyByCode, searchUsers } from '@/api/admin' import { getDashboard, getPointActions, executePointAction, getMatches, getPointOrders, verifyOrder, verifyByCode, searchUsers } from '@/api/admin'
const router = useRouter()
const userStore = useUserStore()
// ID使
const currentStoreId = computed(() => userStore.userInfo?.storeId ?? userStore.userInfo?.store_id ?? '')
// 使
const goToDisplay = (path) => {
const query = {}
if (currentStoreId.value) {
query.store_id = currentStoreId.value
}
router.push({ path, query })
}
const stats = ref({}) const stats = ref({})
const quickActions = ref([]) const quickActions = ref([])
const recentMatches = ref([]) const recentMatches = ref([])

View File

@ -158,6 +158,7 @@
:rules="addLadderRules" :rules="addLadderRules"
ref="addLadderFormRef" ref="addLadderFormRef"
label-width="100px"> label-width="100px">
<!-- 超管可选择门店门店店员自动使用当前门店 -->
<el-form-item <el-form-item
v-if="userStore.isSuperAdmin" v-if="userStore.isSuperAdmin"
label="门店" label="门店"
@ -170,6 +171,9 @@
:value="store.id" /> :value="store.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item v-else label="门店">
<span class="current-store-label">{{ currentStoreName || '当前门店' }}</span>
</el-form-item>
<el-form-item label="手机号" prop="phone"> <el-form-item label="手机号" prop="phone">
<el-input <el-input
v-model="addLadderForm.phone" v-model="addLadderForm.phone"
@ -258,6 +262,10 @@
power_score: 1000, power_score: 1000,
}); });
const currentStoreName = computed(
() => userStore.userInfo?.storeName || ""
);
const addLadderRules = computed(() => ({ const addLadderRules = computed(() => ({
...(userStore.isSuperAdmin ...(userStore.isSuperAdmin
? { ? {
@ -323,8 +331,12 @@
}; };
const handleAddLadderUser = (row) => { const handleAddLadderUser = (row) => {
const storeId =
userStore.isSuperAdmin
? ""
: userStore.userInfo?.storeId ?? userStore.userInfo?.store_id ?? "";
addLadderForm.value = { addLadderForm.value = {
store_id: userStore.isSuperAdmin ? "" : userStore.userInfo?.storeId || "", store_id: storeId,
phone: row.phone || "", phone: row.phone || "",
real_name: row.nickname || "", real_name: row.nickname || "",
gender: row.gender === 1 || row.gender === 2 ? row.gender : 1, gender: row.gender === 1 || row.gender === 2 ? row.gender : 1,
@ -373,5 +385,10 @@
color: var(--text-secondary); color: var(--text-secondary);
} }
} }
.current-store-label {
font-weight: 500;
color: var(--el-text-color-primary);
} }
}
</style> </style>

View File

@ -13,11 +13,11 @@ router.post('/login', adminController.login);
// === 仪表盘 === // === 仪表盘 ===
router.get('/dashboard', authAdmin, adminController.getDashboard); router.get('/dashboard', authAdmin, adminController.getDashboard);
// === 用户管理(超管) === // === 用户管理(超管与门店店员均可访问,店员可添加用户为当前门店天梯用户) ===
router.get('/users', authAdmin, requireSuperAdmin, adminController.getUsers); router.get('/users', authAdmin, adminController.getUsers);
router.get('/users/search', authAdmin, adminController.searchUsers); // 搜索用户(用于积分操作,普通管理员可用) router.get('/users/search', authAdmin, adminController.searchUsers); // 搜索用户(用于积分操作,普通管理员可用)
router.get('/users/:id', authAdmin, requireSuperAdmin, adminController.getUserDetail); router.get('/users/:id', authAdmin, adminController.getUserDetail);
router.put('/users/:id/status', authAdmin, requireSuperAdmin, adminController.updateUserStatus); router.put('/users/:id/status', authAdmin, adminController.updateUserStatus);
// === 门店管理(超管) === // === 门店管理(超管) ===
router.get('/stores', authAdmin, requireSuperAdmin, adminController.getStores); router.get('/stores', authAdmin, requireSuperAdmin, adminController.getStores);