Browse Source

加入抽獎管理 修復pusher

maa3520 2 years ago
parent
commit
9577faa8b1

+ 2 - 2
.env.example

@@ -15,7 +15,7 @@ DB_DATABASE=
 DB_USERNAME=BIMuser
 DB_PASSWORD=Component3444
 
-BROADCAST_DRIVER=log
+BROADCAST_DRIVER=pusher
 CACHE_DRIVER=file
 FILESYSTEM_DISK=local
 QUEUE_CONNECTION=sync
@@ -46,7 +46,7 @@ AWS_USE_PATH_STYLE_ENDPOINT=false
 PUSHER_APP_ID=
 PUSHER_APP_KEY=
 PUSHER_APP_SECRET=
-PUSHER_APP_CLUSTER=mt1
+PUSHER_APP_CLUSTER=ap3
 
 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
 MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

+ 6 - 5
app/Events/TemplateEvent.php

@@ -8,16 +8,17 @@ use Illuminate\Broadcasting\InteractsWithSockets;
 use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
 
 /* Auto generate by : https://dashboard.pusher.com/ */
-class TemplateEvent
+
+class TemplateEvent  implements ShouldBroadcast
 {
     use Dispatchable, InteractsWithSockets, SerializesModels;
-
+    public $message;
     /**
      * Create a new event instance.
      *
      * @return void
      */
-    public function __construct()
+    public function __construct($message)
     {
         //
         $this->message = $message;
@@ -30,11 +31,11 @@ class TemplateEvent
      */
     public function broadcastOn()
     {
-       //
+        //
         return ['my-channel'];
     }
 
-  
+
     public function broadcastAs()
     {
         return 'my-event';

+ 6 - 1
app/Http/Controllers/TemplateController.php

@@ -2,9 +2,11 @@
 
 namespace App\Http\Controllers;
 
+use App\Events\TemplateEvent;
 use App\Models\Template;
 use App\Http\Requests\StoreTemplateRequest;
 use App\Http\Requests\UpdateTemplateRequest;
+use Illuminate\Http\Request;
 
 class TemplateController extends Controller
 {
@@ -47,9 +49,12 @@ class TemplateController extends Controller
      * @param  \App\Models\Template  $template
      * @return \Illuminate\Http\Response
      */
-    public function show(Template $template)
+    public function show(Request $request)
     {
         //
+        event(new TemplateEvent($request->message));
+        //$foo = new TemplateEvent;
+        return $request->message;
     }
 
     /**

+ 2 - 0
resources/js/src/libs/acl/ability.js

@@ -36,12 +36,14 @@ export const defineRulesFor = (user) => {
             cannot('read', 'permission');
             cannot('read', '模板');
             cannot('read', '管理功能');
+            cannot('read', 'slotManager');
             break;
         case 'Draw':
         case 'Checkin':
             can('read', 'all');
             cannot('read', '管理功能');
             cannot('read', 'permission');
+            cannot('read', 'slotManager');
             break;
         case 'Trainer':
             break;

+ 13 - 0
resources/js/src/router/config.js

@@ -145,6 +145,19 @@ export const settings = {
                 requiresAuth: true,
             },
         },
+        {
+            path: '/slotManager/:activity_id',
+            name: 'slotManager',
+            component: () => import('@/views/lottery/SlotManager.vue'),
+            meta: {
+                pageTitle: '抽獎管理',
+                breadcrumb: [{
+                    text: '抽獎管理',
+                    active: true,
+                }, ],
+                requiresAuth: true,
+            },
+        },
         {
             path: '/permission',
             name: 'permission',

+ 0 - 1
resources/js/src/views/lottery/CheckIn.vue

@@ -76,7 +76,6 @@
 <script>
 import { BRow, BCol, BCard, BCardText, BButton, BMedia, BFormInput, BImg, BMediaAside, BMediaBody, BInputGroup, BInputGroupAppend } from 'bootstrap-vue'
 import Ripple from 'vue-ripple-directive'
-import Pusher from 'pusher-js';
 import useJwt from '@/auth/jwt/useJwt'
 import ToastificationContent from '@core/components/toastification/ToastificationContent.vue'
 import { avatarText } from '@core/utils/filter';

+ 2 - 2
resources/js/src/views/lottery/Draw.vue

@@ -5,7 +5,7 @@
         <b-card title="獎項列表" style="height:75vh;">
           <b-table responsive hover sticky-header=true :items="getPrizeList(prizeList)" bordered :fields="fields"
             @row-clicked="click" style="max-height: 57vh; overflow: auto;">
-            <template #cell(ICON)="data" class="text-center">
+            <template #cell(ICON)="data">
               <div class="text-center">
                 <feather-icon :icon="data.value" />
               </div>
@@ -102,7 +102,7 @@
         <b-card title="抽獎紀錄" style="height:75vh;">
           <b-table responsive sticky-header=true :items="getRecipientsList(recipientsList)" bordered
             style="max-height: 65vh; overflow: auto;">
-            <template #cell(ICON)="data" class="text-center">
+            <template #cell(ICON)="data">
               <div class="text-center">
                 <feather-icon :icon="data.value" />
               </div>

+ 17 - 1
resources/js/src/views/lottery/Slot.vue

@@ -118,6 +118,8 @@ import vSelect from 'vue-select'
 import { VueGoodTable } from 'vue-good-table'
 import 'vue-select/dist/vue-select.css';
 
+import Pusher from 'pusher-js';
+
 export default {
 	components: {
 		BRow,
@@ -207,10 +209,24 @@ export default {
 				"塭仔圳專案",
 				"萬大市場專案",
 				"來賓"
-			]
+			],
+			messages:[],
 		}
 	},
 	created() {
+
+		Pusher.logToConsole = true;
+
+		var pusher = new Pusher('9fb7f6f0141efc7e1293', {
+			cluster: 'ap3'
+		});
+
+		var channel = pusher.subscribe('my-channel');
+		channel.bind('my-event', function (data) {
+			console.log(data);
+			// app.messages.push(JSON.stringify(data));
+		});
+
 		useJwt.postData('/api/activity/show', { activity_id: this.activityId }).then(res => {
 			this.activity = res.data;
 			this.activityName = res.data.activity_name

+ 421 - 0
resources/js/src/views/lottery/SlotManager.vue

@@ -0,0 +1,421 @@
+<template>
+	<div>
+		<b-row class="prize-list-wrapper">
+			<b-col md="6">
+				<b-card title="獎項列表" style="height:75vh;">
+					<b-table responsive hover sticky-header=true :items="getPrizeList(prizeList)" bordered :fields="fields"
+						@row-clicked="click" style="max-height: 57vh; overflow: auto;">
+						<template #cell(ICON)="data">
+							<div class="text-center">
+								<feather-icon :icon="data.value" />
+							</div>
+						</template>
+					</b-table>
+					<b-button v-ripple.400="'rgba(255, 255, 255, 0.15)'" class="mt-3" variant="outline-primary" block
+						@click="showAddPrize">
+						加碼
+					</b-button>
+				</b-card>
+			</b-col>
+
+			<b-modal ref="draw-modal" id="draw-modal" centered title="抽獎" no-stacking hide-footer>
+				<label>獎品:</label>
+				<b-form-input v-model="prize" :disabled=true />
+				<br>
+				<label>地區: </label>
+				<v-select multiple v-model="region" :options="regionOption" />
+				<br>
+				<label>數量: </label>
+				<div class="demo-inline-spacing">
+					<b-form-radio v-model="drawOne" name="some-radios" :value=true>
+						單抽
+					</b-form-radio>
+					<b-form-radio v-model="drawOne" name="some-radios" :value=false>
+						全抽
+					</b-form-radio>
+				</div>
+
+				<b-button v-ripple.400="'rgba(255, 255, 255, 0.15)'" class="mt-3" variant="outline-secondary" block
+					@click="hideModal">
+					Close
+				</b-button>
+				<b-button v-ripple.400="'rgba(255, 255, 255, 0.15)'" class="mt-2" variant="primary" block @click="draw">
+					Accept
+				</b-button>
+			</b-modal>
+
+			<b-modal ref="draw-error" centered title="抽獎錯誤" no-stacking hide-footer>
+				<b-card-text class="my-1 text-center">
+					抽獎錯誤
+				</b-card-text>
+				<b-button v-ripple.400="'rgba(255, 255, 255, 0.15)'" class="mt-3" variant="outline-secondary" block
+					@click="hideModal">
+					Close
+				</b-button>
+			</b-modal>
+
+			<b-modal ref="draw-animation" title="得獎者是......" centered no-stacking hide-footer>
+				<br>
+				<div :key="index" v-for="(item, index) in drawList" :class=counterId(index)>
+					<i :datafinal=item.department>部門</i>
+					<div :datafinal=item.userId>
+						<i>0</i>
+						<i>0</i>
+						<i>0</i>
+						<i>0</i>
+						<span :datafinal=item.userName></span>
+					</div>
+				</div>
+
+				<div v-if="drawOne">
+					<b-button v-ripple.400="'rgba(255, 255, 255, 0.15)'" class="mt-2" variant="outline-primary" block
+						@click="donate">
+						捐出
+					</b-button>
+				</div>
+
+				<b-button v-ripple.400="'rgba(255, 255, 255, 0.15)'" class="mt-2" variant="primary" block @click="store">
+					Accept
+				</b-button>
+			</b-modal>
+
+			<b-modal ref="add-prize" centered title="加碼" no-stacking hide-footer>
+				<label>獎品:</label>
+				<b-form-input v-model="addPrizeName" />
+				<br>
+				<label>加碼者: </label>
+				<b-form-input v-model="addPrizeUser" />
+				<br>
+				<label>數量: </label>
+				<b-form-spinbutton v-model="addPrizeNumber" min="1" />
+
+				<b-button v-ripple.400="'rgba(255, 255, 255, 0.15)'" class="mt-3" variant="outline-secondary" block
+					@click="hideModal">
+					Close
+				</b-button>
+				<b-button v-ripple.400="'rgba(255, 255, 255, 0.15)'" class="mt-2" variant="primary" block @click="addPrize">
+					Accept
+				</b-button>
+			</b-modal>
+
+			<b-col md="6">
+				<b-card title="抽獎紀錄" style="height:75vh;">
+					<b-table responsive sticky-header=true :items="getRecipientsList(recipientsList)" bordered
+						style="max-height: 65vh; overflow: auto;">
+						<template #cell(ICON)="data">
+							<div class="text-center">
+								<feather-icon :icon="data.value" />
+							</div>
+						</template>
+					</b-table>
+				</b-card>
+			</b-col>
+		</b-row>
+	</div>
+</template>
+  
+<script>
+import { BRow, BCol, BCard, BCardText, BLink, BTable, BModal, BFormInput, BFormSpinbutton, BButton, BFormCheckbox, BFormRadio } from 'bootstrap-vue'
+import useJwt from '@/auth/jwt/useJwt'
+import { VueGoodTable } from 'vue-good-table'
+import vSelect from 'vue-select'
+import Ripple from 'vue-ripple-directive'
+import 'vue-select/dist/vue-select.css';
+
+export default {
+	components: {
+		BRow,
+		BCol,
+		BCard,
+		BCardText,
+		BLink,
+		BTable,
+		BModal,
+		BFormInput,
+		BFormSpinbutton,
+		BButton,
+		BFormCheckbox,
+		BFormRadio,
+		VueGoodTable,
+		vSelect,
+	},
+	directives: {
+		Ripple,
+	},
+	data() {
+		return {
+			activityId: this.$route.params.activity_id,
+			activity: {},
+			userList: [],
+			departmentList: [],
+			prizeList: [],
+			recipientsList: [],
+
+			fields: ['獎項', '數量'],
+			regionOption: ['北區', '中區', '南區', '來賓'],
+			region: ['北區', '中區', '南區', '來賓'],
+			drawMax: 1,
+			drawOne: true,
+
+			prize: '',
+			prizeId: '',
+			drawList: [],
+
+			addPrizeName: '',
+			addPrizeUser: '',
+			addPrizeNumber: 1,
+
+			departments: [
+				"行管部庶務科",
+				"人力資源科",
+				"行管部文書科",
+				"南區辦事處",
+				"中區辦事處",
+				"工程管理一部",
+				"工程管理二部",
+				"發展與企畫部",
+				"工程製圖科",
+				"工務部",
+				"大地工程部",
+				"財務部",
+				"運輸土木部",
+				"城鄉發展部",
+				"軌道工程部",
+				"董事長室",
+				"總經理室",
+				"企業發展中心",
+				"成本中心",
+				"資訊中心",
+				"環境水務部",
+				"環境永續部",
+				"機電工程部",
+				"結構工程部",
+				"工程設計群",
+				"工程監理群",
+				"總管理處",
+				"建築及設施群",
+				"數位發展部",
+				"淡海輕軌專案",
+				"安坑輕軌專案",
+				"先進技術中心",
+				"塭仔圳專案",
+				"萬大市場專案",
+				"來賓"
+			]
+		}
+	},
+	created() {
+
+
+		useJwt.postData('/api/activity/show', { activity_id: this.activityId }).then(res => {
+			this.activity = res.data;
+		});
+
+		useJwt.postData('/api/check_in/index_by_activity', { activity_id: this.activityId }).then(res => {
+			this.userList = res.data;
+		});
+
+		useJwt.postData('/api/prize/index_by_activity', { activity_id: this.activityId }).then(res => {
+			this.prizeList = res.data;
+		});
+
+		useJwt.postData('/api/recipients/index_by_activity', { activity_id: this.activityId }).then(res => {
+			this.recipientsList = res.data;
+		});
+
+		useJwt.postData('/api/department/index').then(res => {
+			this.departmentList = res.data;
+		});
+	},
+	methods: {
+		dateFormat(date) {
+			var d = new Date(date);
+			return d.getFullYear()
+				+ '/' + (d.getMonth() + 1).toString().padStart(2, '0')
+				+ '/' + d.getDay().toString().padStart(2, '0')
+				+ '/' + d.getHours().toString().padStart(2, '0')
+				+ ':' + d.getMinutes().toString().padStart(2, '0')
+				+ ':' + d.getSeconds().toString().padStart(2, '0');
+		},
+		getPrizeList(prizeList) {
+			var output = [];
+			prizeList.forEach(element => {
+				output.push({ id: element.id, '獎項': element.name, '數量': element.count });
+			});
+			return output;
+		},
+		getPrizeName(prizeId) {
+			var output = '';
+			this.prizeList.forEach(element => {
+				if (prizeId == element.id) {
+					output = element.name;
+				}
+			});
+			return output;
+		},
+		getRecipientsList(recipientsList) {
+			var output = [];
+			recipientsList.forEach(element => {
+				output.push({ '獎項': this.getPrizeName(element.prize_id), '獲獎人': this.userList[element.user_id], '時間': this.dateFormat(element.created_at) });
+			});
+			return output;
+		},
+		counterId(id) {
+			return "counter_" + id;
+		},
+		drawNumber() {
+			if (this.drawOne) {
+				return 1;
+			}
+			return this.drawMax;
+		},
+		showDraw() {
+			this.$refs['draw-modal'].show();
+		},
+		showDrawAnimation() {
+			this.$refs['draw-animation'].show();
+		},
+		showAddPrize() {
+			this.$refs['add-prize'].show();
+		},
+		showDrawError() {
+			this.$refs['draw-error'].show();
+		},
+		hideModal() {
+			this.$refs['draw-modal'].hide();
+			this.$refs['draw-error'].hide();
+			this.$refs['draw-animation'].hide();
+			this.$refs['add-prize'].hide();
+			this.updateDataset();
+		},
+		updateDataset() {
+			useJwt.postData('/api/prize/index_by_activity', { activity_id: this.activityId }).then(res => {
+				this.prizeList = res.data;
+			});
+			useJwt.postData('/api/recipients/index_by_activity', { activity_id: this.activityId }).then(res => {
+				this.recipientsList = res.data;
+			});
+		},
+		click(params) {
+			this.prize = this.getPrizeName(params.id);
+			this.prizeId = params.id;
+			this.drawOne = true;
+			this.drawMax = params["數量"];
+			this.drawList = [];
+			if (this.drawMax == 0) {
+				this.showDrawError();
+			} else {
+				this.showDraw();
+			}
+		},
+		draw() {
+			useJwt.postData('/api/draw/draw', {
+				activity_id: this.activityId,
+				prize_id: this.prizeId,
+				region: this.region,
+				number: this.drawNumber()
+			}).then(res => {
+				if (res.data) {
+					this.drawList = res.data;
+					setTimeout(() => {
+						this.count();
+					}, 500);
+					this.showDrawAnimation();
+				} else {
+					this.showDrawError();
+					console.log("draw error");
+				}
+			});
+		},
+		store() {
+			var idList = [];
+			this.drawList.forEach(element => {
+				idList.push(element.user_id);
+			});
+			useJwt.postData('/api/draw/store', {
+				users: idList,
+				prize_id: String(this.prizeId),
+				number: this.drawNumber()
+			}).then(res => {
+				if (res.data) {
+					console.log("store success");
+				} else {
+					console.log("store error");
+				}
+				this.hideModal();
+			});
+		},
+		donate() {
+			var idList = [];
+			this.drawList.forEach(element => {
+				idList.push(element.user_id);
+			});
+			useJwt.postData('/api/draw/donate', { users: idList }).then(res => {
+				if (res.data) {
+					console.log("donate success");
+				} else {
+					console.log("donate error");
+				}
+				this.hideModal();
+			});
+		},
+		addPrize() {
+			useJwt.postData('/api/prize/store', {
+				activity_id: this.activityId,
+				name: this.addPrizeName,
+				provider: this.addPrizeUser,
+				count: this.addPrizeNumber
+			}).then(res => {
+				if (res.data) {
+					console.log("add prize success");
+				} else {
+					console.log("add prize error");
+				}
+				this.hideModal();
+			});
+		},
+		count() {
+			var departments = this.departments;
+			var numbers = "0123456789"
+			var string = numbers;
+
+			this.drawList.forEach((element, index) => {
+				var departmentName = this.departmentList[element.department_id];
+				var deps = document.querySelectorAll(".counter_" + index + " > i");
+				var allCounters = document.querySelectorAll(".counter_" + index + " > div > i");
+
+				deps.forEach(function (el) {
+					var duration = 1000;
+					var interval = setInterval(function () {
+						el.innerText = departments[Math.floor(Math.random() * departments.length)];
+						duration = duration - 50;
+						if (duration <= 0) {
+							clearInterval(interval);
+							el.innerText = departmentName;
+						}
+					}, 50);
+				});
+				allCounters.forEach((el, i) => {
+					var duration = 500 + i * 1000;
+					var interval = setInterval(e => {
+						el.innerText = string.charAt(Math.random() * string.length);
+						duration = duration - 50;
+						if (duration <= 0) {
+							clearInterval(interval);
+							el.innerText = element.user_id[i];
+							if (i == 3) {
+								document.querySelectorAll(".counter_" + index + " > div > span")[0].innerText = element.user_name;
+							}
+						}
+					}, 50);
+				});
+			});
+		},
+	},
+}
+</script>
+  
+<style lang="scss">
+@import '~@resources/scss/vue/libs/vue-select.scss';
+</style>
+  

+ 1 - 1
routes/api.php

@@ -86,7 +86,7 @@ Route::post('/refresh', function (Request $request) {
 Route::post('/index', [TemplateController::class, 'index']);
 
 Route::middleware('auth:sanctum')->group(function () {
-
+    Route::post('/test/pusher', [TemplateController::class, 'show']);
 });
 
 Route::middleware(['auth:sanctum', 'abilities:Admin'])->group(function () {

+ 5 - 0
routes/console.php

@@ -1,5 +1,6 @@
 <?php
 
+use App\Events\TemplateEvent;
 use Illuminate\Foundation\Inspiring;
 use Illuminate\Support\Facades\Artisan;
 
@@ -17,3 +18,7 @@ use Illuminate\Support\Facades\Artisan;
 Artisan::command('inspire', function () {
     $this->comment(Inspiring::quote());
 })->purpose('Display an inspiring quote');
+Artisan::command('pusher', function () {
+    event(new TemplateEvent('hello world'));
+})->purpose('Sending pusher test');
+