add include Role

This commit is contained in:
KarolChang 2022-04-16 13:37:36 +08:00
parent 3a335f578a
commit 0d24a3b730
26 changed files with 179 additions and 158 deletions

View File

@ -21,7 +21,6 @@ git commit -m 'deploy'
# if you are deploying to https://<USERNAME>.Github.io/<REPO> # if you are deploying to https://<USERNAME>.Github.io/<REPO>
# git push -f https://github.com/<USERNAME>/<REPO>.git master:gh-pages # git push -f https://github.com/<USERNAME>/<REPO>.git master:gh-pages
git push origin master
git push -f https://github.com/KarolChang/jm-expense-vue-ts.git master:gh-pages git push -f https://github.com/KarolChang/jm-expense-vue-ts.git master:gh-pages
cd - cd -

11
package-lock.json generated
View File

@ -661,6 +661,11 @@
"fastq": "^1.6.0" "fastq": "^1.6.0"
} }
}, },
"@popperjs/core": {
"version": "2.11.5",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
"integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw=="
},
"@protobufjs/aspromise": { "@protobufjs/aspromise": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@ -2687,9 +2692,9 @@
"dev": true "dev": true
}, },
"sweetalert2": { "sweetalert2": {
"version": "11.3.0", "version": "11.4.8",
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.3.0.tgz", "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz",
"integrity": "sha512-C0TFp0VLxgx+PmhJ0mL8qzx+iYjnCLdDbvQHKY6KAGI+xwawMvLkStPgw2LmJl6itaDhR/qLQStPFIbr1VK9Ow==" "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA=="
}, },
"table": { "table": {
"version": "5.4.6", "version": "5.4.6",

View File

@ -9,6 +9,7 @@
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src" "lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src"
}, },
"dependencies": { "dependencies": {
"@popperjs/core": "^2.11.5",
"@vueform/multiselect": "^2.3.1", "@vueform/multiselect": "^2.3.1",
"axios": "^0.24.0", "axios": "^0.24.0",
"bootstrap": "^5.1.3", "bootstrap": "^5.1.3",
@ -21,7 +22,7 @@
"firebase": "^9.6.3", "firebase": "^9.6.3",
"luxon": "^1.0.0", "luxon": "^1.0.0",
"pinia": "^2.0.6", "pinia": "^2.0.6",
"sweetalert2": "^11.3.0", "sweetalert2": "^11.4.8",
"vue": "^3.2.29", "vue": "^3.2.29",
"vue-class-component": "^8.0.0-0", "vue-class-component": "^8.0.0-0",
"vue-router": "^4.0.0-0", "vue-router": "^4.0.0-0",

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue' import { inject } from 'vue'
import Swal from 'sweetalert2' import { Swal, Toast, ConfirmBox } from '@/utils/swal'
import { Toast, ConfirmBox } from '@/utils/swal'
import expenseAPI from '@/apis/expense' import expenseAPI from '@/apis/expense'
import { CategoryInput } from '@/models' import { CategoryInput } from '@/models'

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue' import { inject } from 'vue'
import Swal from 'sweetalert2' import { Swal, Toast, ConfirmBox } from '@/utils/swal'
import { Toast, ConfirmBox } from '@/utils/swal'
import expenseAPI from '@/apis/expense' import expenseAPI from '@/apis/expense'
import { Category, CategoryInput } from '@/models' import { Category, CategoryInput } from '@/models'

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject, computed, Ref } from 'vue' import { inject, computed, Ref } from 'vue'
import Swal from 'sweetalert2' import { Swal, Toast, ConfirmBox } from '@/utils/swal'
import { Toast, ConfirmBox } from '@/utils/swal'
import expenseAPI from '@/apis/expense' import expenseAPI from '@/apis/expense'
import { ExpenseInput, Category } from '@/models' import { ExpenseInput, Category } from '@/models'
import { useStore } from '@/store/index' import { useStore } from '@/store/index'
@ -75,9 +74,12 @@ const btnClick = async () => {
if (!item || !amount || !date) { if (!item || !amount || !date) {
Swal.showValidationMessage('除了[備註],所有資料都是必填!') Swal.showValidationMessage('除了[備註],所有資料都是必填!')
} }
console.log('store.currentUser', store.currentUser)
console.log('store.currentUser?.id', store.currentUser?.id)
if (store.currentUser) {
return { return {
input: { input: {
UserId: store.currentUser?.id, UserId: store.currentUser.id,
CategoryId: Number(categoryId), CategoryId: Number(categoryId),
item, item,
amount, amount,
@ -85,9 +87,15 @@ const btnClick = async () => {
date date
} as ExpenseInput } as ExpenseInput
} }
} else {
Toast.fire({
icon: 'error',
title: '無法取得使用者ID'
})
}
} }
}) })
if (formValues) { if (formValues?.input) {
createExpense(formValues) createExpense(formValues)
} }
} catch (error) { } catch (error) {

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject, Ref } from 'vue' import { computed, inject, Ref } from 'vue'
import Swal from 'sweetalert2' import { Swal, Toast, ConfirmBox } from '@/utils/swal'
import { Toast, ConfirmBox } from '@/utils/swal'
import expenseAPI from '@/apis/expense' import expenseAPI from '@/apis/expense'
import { Expense, ExpenseInput, Category } from '@/models' import { Expense, ExpenseInput, Category } from '@/models'
import { useStore } from '@/store/index' import { useStore } from '@/store/index'
@ -88,9 +87,10 @@ const btnClick = async () => {
if (!item || !amount || !date) { if (!item || !amount || !date) {
Swal.showValidationMessage('除了[備註],所有資料都是必填!') Swal.showValidationMessage('除了[備註],所有資料都是必填!')
} }
if (store.currentUser) {
return { return {
input: { input: {
UserId: store.currentUser?.id, UserId: store.currentUser.id,
CategoryId: Number(categoryId), CategoryId: Number(categoryId),
item, item,
amount, amount,
@ -98,9 +98,16 @@ const btnClick = async () => {
date date
} as ExpenseInput } as ExpenseInput
} }
} else {
Toast.fire({
icon: 'error',
title: '無法取得使用者ID'
})
}
} }
}) })
if (formValues) { console.log('formValues', formValues)
if (formValues?.input) {
editExpense(formValues) editExpense(formValues)
} }
} catch (error) { } catch (error) {

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject, computed } from 'vue' import { inject, computed } from 'vue'
import Swal from 'sweetalert2' import { Swal, Toast, ConfirmBox } from '@/utils/swal'
import { Toast, ConfirmBox } from '@/utils/swal'
import userAPI from '@/apis/user' import userAPI from '@/apis/user'
import { PermissionInput, ActionTypePermission } from '@/models' import { PermissionInput, ActionTypePermission } from '@/models'
const actions: ActionTypePermission[] = ['查看', '新增', '編輯', '刪除', '停用', '操作'] const actions: ActionTypePermission[] = ['查看', '新增', '編輯', '刪除', '停用', '操作']

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject, computed } from 'vue' import { inject, computed } from 'vue'
import Swal from 'sweetalert2' import { Swal, Toast, ConfirmBox } from '@/utils/swal'
import { Toast, ConfirmBox } from '@/utils/swal'
import userAPI from '@/apis/user' import userAPI from '@/apis/user'
import { Permission, PermissionInput, ActionTypePermission } from '@/models' import { Permission, PermissionInput, ActionTypePermission } from '@/models'
const actions: ActionTypePermission[] = ['查看', '新增', '編輯', '刪除', '停用', '操作'] const actions: ActionTypePermission[] = ['查看', '新增', '編輯', '刪除', '停用', '操作']

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue' import { inject } from 'vue'
import Swal from 'sweetalert2' import { Swal, Toast, ConfirmBox } from '@/utils/swal'
import { Toast, ConfirmBox } from '@/utils/swal'
import recordAPI from '@/apis/record' import recordAPI from '@/apis/record'
import { pushMsgToBoth } from '@/utils/lineBotMsg' import { pushMsgToBoth } from '@/utils/lineBotMsg'
import { RecordInput } from '@/models' import { RecordInput } from '@/models'
@ -66,6 +65,7 @@ const btnClick = async () => {
if (!item || !merchant || !amount || !date) { if (!item || !merchant || !amount || !date) {
Swal.showValidationMessage('所有資料都是必填!若紀錄者為空,請登入~') Swal.showValidationMessage('所有資料都是必填!若紀錄者為空,請登入~')
} }
if (store.currentUser) {
return { return {
input: { input: {
item, item,
@ -75,9 +75,15 @@ const btnClick = async () => {
UserId: store.currentUser?.id UserId: store.currentUser?.id
} as RecordInput } as RecordInput
} }
} else {
Toast.fire({
icon: 'error',
title: '無法取得使用者ID'
})
}
} }
}) })
if (formValues) { if (formValues?.input) {
createRecord(formValues) createRecord(formValues)
} }
} catch (error) { } catch (error) {

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue' import { inject } from 'vue'
import Swal from 'sweetalert2' import { Swal, Toast, ConfirmBox } from '@/utils/swal'
import { Toast, ConfirmBox } from '@/utils/swal'
import recordAPI from '@/apis/record' import recordAPI from '@/apis/record'
import { pushMsgToBoth } from '@/utils/lineBotMsg' import { pushMsgToBoth } from '@/utils/lineBotMsg'
import { Record, RecordInput } from '@/models' import { Record, RecordInput } from '@/models'
@ -65,6 +64,7 @@ const btnClick = async () => {
if (!item || !merchant || !amount || !date) { if (!item || !merchant || !amount || !date) {
Swal.showValidationMessage('所有資料都是必填!若編輯者為空,請登入~') Swal.showValidationMessage('所有資料都是必填!若編輯者為空,請登入~')
} }
if (store.currentUser) {
return { return {
id: record.id as number, id: record.id as number,
input: { input: {
@ -72,12 +72,18 @@ const btnClick = async () => {
merchant, merchant,
amount, amount,
date, date,
UserId: store.currentUser?.id UserId: store.currentUser.id
} as RecordInput } as RecordInput
} }
} else {
Toast.fire({
icon: 'error',
title: '無法取得使用者ID'
})
}
} }
}) })
if (formValues) { if (formValues?.input) {
editRecord(formValues) editRecord(formValues)
} }
} catch (error) { } catch (error) {

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue' import { inject } from 'vue'
import Swal from 'sweetalert2' import { Swal, Toast, ConfirmBox } from '@/utils/swal'
import { Toast, ConfirmBox } from '@/utils/swal'
import userAPI from '@/apis/user' import userAPI from '@/apis/user'
import { RoleInput } from '@/models' import { RoleInput } from '@/models'

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue' import { inject } from 'vue'
import Swal from 'sweetalert2' import { Swal, Toast, ConfirmBox } from '@/utils/swal'
import { Toast, ConfirmBox } from '@/utils/swal'
import userAPI from '@/apis/user' import userAPI from '@/apis/user'
import { Role, RoleInput } from '@/models' import { Role, RoleInput } from '@/models'

View File

@ -114,6 +114,7 @@ const openUserRP = () => {
</ul> </ul>
</div> </div>
</div> </div>
<!-- <transition name="slide-right"> <!-- <transition name="slide-right">
<UserRP v-show="userRPOpen" /> <UserRP v-show="userRPOpen" />
</transition> --> </transition> -->

View File

@ -1,6 +1,6 @@
// import Vue from 'vue' // import Vue from 'vue'
import { createApp } from 'vue' import { createApp } from 'vue'
import App from '@/App.vue' import App from '@/views/App.vue'
function capitalizeFirstLetter(string: string) { function capitalizeFirstLetter(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1) return string.charAt(0).toUpperCase() + string.slice(1)

View File

@ -20,7 +20,7 @@ export const getFirebaseUser = () => {
console.log(`[firebase] onAuthStateChanged`) console.log(`[firebase] onAuthStateChanged`)
if (user) { if (user) {
console.log('[auth] Get Firebase User', user) console.log('[auth] Get Firebase User', user)
store.login(user) await store.login(user)
} else { } else {
store.logout() store.logout()
} }

View File

@ -1,6 +1,6 @@
import { initializeApp } from 'firebase/app' import { initializeApp, FirebaseOptions } from 'firebase/app'
const firebaseConfig = { const firebaseConfig: FirebaseOptions = {
apiKey: import.meta.env.VITE_FIREBASE_API_KEY, apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTO_DOMAIN, authDomain: import.meta.env.VITE_FIREBASE_AUTO_DOMAIN,
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
@ -8,7 +8,7 @@ const firebaseConfig = {
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
appId: import.meta.env.VITE_FIREBASE_APP_ID, appId: import.meta.env.VITE_FIREBASE_APP_ID,
measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID
} } as FirebaseOptions
export const initFirebase = () => { export const initFirebase = () => {
initializeApp(firebaseConfig) initializeApp(firebaseConfig)

View File

@ -1,5 +1,5 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import App from './App.vue' import App from './views/App.vue'
import router from './router' import router from './router'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import { getFirebaseUser } from '@/firebase/auth' import { getFirebaseUser } from '@/firebase/auth'
@ -8,6 +8,7 @@ import { initFirebase } from '@/firebase/config'
import Datepicker from 'vue3-date-time-picker' import Datepicker from 'vue3-date-time-picker'
import 'vue3-date-time-picker/dist/main.css' import 'vue3-date-time-picker/dist/main.css'
import '@popperjs/core'
import 'bootstrap' import 'bootstrap'
import 'bootstrap/dist/css/bootstrap.min.css' import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootswatch/dist/minty/bootstrap.min.css' import 'bootswatch/dist/minty/bootstrap.min.css'

View File

@ -4,7 +4,7 @@ export class Category {
id!: number id!: number
name!: string name!: string
icon!: string icon!: string
// photoUrl?: string photoUrl!: string | null
type!: CategoryType type!: CategoryType
deletedAt!: Date | null deletedAt!: Date | null
createdAt!: Date createdAt!: Date

View File

@ -1,7 +1,7 @@
import { Toast } from '@/utils/swal' import { Toast } from '@/utils/swal'
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import { useStore } from '../store/index' import { useStore } from '../store/index'
import Default from '@/views/Default.vue' import AppLayout from '@/views/App.vue'
const root = '/jm-expense-vue-ts' const root = '/jm-expense-vue-ts'
@ -73,15 +73,15 @@ export const routes: RouteRecordRaw[] = [
show: true show: true
} }
}, },
{ // {
path: `${root}/tools`, // path: `${root}/tools`,
name: 'Tools', // name: 'Tools',
component: () => import('../views/Tools.vue'), // component: () => import('../views/Tools.vue'),
meta: { // meta: {
pageTitle: '小工具', // pageTitle: '小工具',
show: true // show: true
} // }
}, // },
{ {
path: `${root}/game`, path: `${root}/game`,
name: 'Game', name: 'Game',
@ -92,15 +92,15 @@ export const routes: RouteRecordRaw[] = [
// auth: ['root', 'admin', 'member'] // auth: ['root', 'admin', 'member']
} }
}, },
{ // {
path: `${root}/admin`, // path: `${root}/admin`,
name: 'Admin', // name: 'Admin',
redirect: { name: 'Admin-Role' }, // redirect: { name: 'Admin-Role' },
component: Default, // component: AppLayout,
meta: { // meta: {
pageTitle: '管理面板', // pageTitle: '管理面板',
show: true // show: true
} // },
// children: [ // children: [
// { // {
// path: 'role', // path: 'role',
@ -121,45 +121,34 @@ export const routes: RouteRecordRaw[] = [
// } // }
// } // }
// ] // ]
}, // },
{
path: '/admin/role',
name: 'Admin-Role',
component: () => import('@/views/Role.vue'),
meta: {
pageTitle: '角色管理',
show: true
}
// children: [
// { // {
// path: ':id/access', // path: `${root}/admin/role`,
// name: 'Admin-Role',
// component: () => import('@/views/Role.vue'),
// meta: {
// pageTitle: '角色管理',
// show: true
// }
// },
// {
// path: '/admin/role/:id/access',
// name: 'Admin-Role-Access', // name: 'Admin-Role-Access',
// component: () => import('@/views/Access.vue'), // component: () => import('@/views/Access.vue'),
// meta: { // meta: {
// pageTitle: '角色管理 / 設置權限[角色名稱]', // pageTitle: '角色管理 / 設置權限[角色名稱]',
// show: false // show: false
// } // }
// },
// {
// path: '/admin/permission',
// name: 'Admin-Permission',
// component: () => import('../views/Permission.vue'),
// meta: {
// pageTitle: '權限管理',
// show: true
// } // }
// ] // },
},
{
path: '/admin/role/:id/access',
name: 'Admin-Role-Access',
component: () => import('@/views/Access.vue'),
meta: {
pageTitle: '角色管理 / 設置權限[角色名稱]',
show: false
}
},
{
path: '/admin/permission',
name: 'Admin-Permission',
component: () => import('../views/Permission.vue'),
meta: {
pageTitle: '權限管理',
show: true
}
},
{ {
path: '/:pathMatch(.*)*', path: '/:pathMatch(.*)*',
name: 'NotFound', name: 'NotFound',

View File

@ -43,9 +43,9 @@ export const useStore = defineStore('index', {
console.error('error') console.error('error')
} }
}, },
login(user: FirebaseUser) { async login(user: FirebaseUser) {
this.firebaseUser = user this.firebaseUser = user
this.getCurrentUser(user.email!) await this.getCurrentUser(user.email!)
}, },
logout() { logout() {
this.firebaseUser = null this.firebaseUser = null

View File

@ -1,4 +1,5 @@
import Swal from 'sweetalert2' import Swal from 'sweetalert2/dist/sweetalert2.js'
import 'sweetalert2/dist/sweetalert2.css'
export const Toast = Swal.mixin({ export const Toast = Swal.mixin({
toast: true, toast: true,
@ -11,3 +12,5 @@ export const ConfirmBox = Swal.mixin({
showConfirmButton: true, showConfirmButton: true,
showCancelButton: true showCancelButton: true
}) })
export { Swal }

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import Navbar from '@/components/Navbar.vue' import Navbar from '@/components/Navbar.vue'
import { onLoad } from './cocos/config' import { onLoad } from '../cocos/config'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
const route = useRoute() const route = useRoute()
@ -11,7 +11,6 @@ if (import.meta.env.NODE_ENV === 'production') {
</script> </script>
<template> <template>
<!-- <div> -->
<Navbar> <Navbar>
<template #main> <template #main>
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
@ -21,7 +20,6 @@ if (import.meta.env.NODE_ENV === 'production') {
</router-view> </router-view>
</template> </template>
</Navbar> </Navbar>
<!-- </div> -->
</template> </template>
<style> <style>
@import url('https://fonts.googleapis.com/css2?family=Zen+Maru+Gothic:wght@400;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Zen+Maru+Gothic:wght@400;700&display=swap');

View File

@ -127,7 +127,7 @@ provide('categoriesByType', categoriesByType)
</div> </div>
<!-- filter --> <!-- filter -->
<div class="d-flex mb-4" style="width: 100%"> <div class="d-flex mb-4" style="width: 100%">
<div class="mt-4" style="width: 50px"> <div class="mt-4" style="width: 100px">
<label for="type" style="float: left; font-size: 0.7em">TYPE</label> <label for="type" style="float: left; font-size: 0.7em">TYPE</label>
<select class="form-select" id="type" aria-label="Default select example" v-model="selectedType"> <select class="form-select" id="type" aria-label="Default select example" v-model="selectedType">
<option selected>ALL</option> <option selected>ALL</option>

View File

@ -49,13 +49,13 @@ provide('refetchRoles', fetchRoles)
<td>{{ role.name }}</td> <td>{{ role.name }}</td>
<td>{{ role.name_en }}</td> <td>{{ role.name_en }}</td>
<td>{{ role.deletedAt === null ? 'V' : 'X' }}</td> <td>{{ role.deletedAt === null ? 'V' : 'X' }}</td>
<td> <!-- <td>
<router-link :to="{ name: 'Admin-Role-Access', params: { id: role.id } }"> <router-link :to="{ name: 'Admin-Role-Access', params: { id: role.id } }">
<i class="fa-solid fa-circle-question"></i <i class="fa-solid fa-circle-question"></i
></router-link> ></router-link>
<EditRoleModalButton :role="role" class="ms-2" /> <EditRoleModalButton :role="role" class="ms-2" />
<DeleteRoleModalButton :role="role" class="ms-2" /> <DeleteRoleModalButton :role="role" class="ms-2" />
</td> </td> -->
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -11,5 +11,8 @@ export default defineConfig({
alias: { alias: {
'@': path.resolve(__dirname, './src') '@': path.resolve(__dirname, './src')
} }
},
server: {
port: 8080
} }
}) })