diff --git a/app/Http/Controllers/ProgramAppendConstructController.php b/app/Http/Controllers/ProgramAppendConstructController.php index d314c7b..328f6f6 100644 --- a/app/Http/Controllers/ProgramAppendConstructController.php +++ b/app/Http/Controllers/ProgramAppendConstructController.php @@ -59,4 +59,12 @@ class ProgramAppendConstructController extends BaseController ])); } + public function from_list(Request $request) { + $query = ProgramAppends::query()->groupBy("from")->select("from")->selectRaw("count(1) as total_count")->orderByDesc("total_count"); + if ($request->has("value")) { + $keyword = $request->get("value"); + $query->where("from", "like", "%{$keyword}%"); + } + return $query->get()->toArray(); + } } diff --git a/public/js/.gitignore b/public/js/.gitignore index a6c7c28..7bf285b 100644 --- a/public/js/.gitignore +++ b/public/js/.gitignore @@ -1 +1,3 @@ *.js +*.js.LICENSE.txt + diff --git a/resources/css/vendor/picomplete/picomplete.css b/resources/css/vendor/picomplete/picomplete.css new file mode 100644 index 0000000..db67af4 --- /dev/null +++ b/resources/css/vendor/picomplete/picomplete.css @@ -0,0 +1,39 @@ +.picomplete { + position: relative; + display: inline-block; +} + +.picomplete-items { + position: absolute; + border: 1px solid #d4d4d4; + border-bottom: none; + border-top: none; + z-index: 99; + + overflow-y: scroll; + max-height: 185px; + scrollbar-width: thin !important; +} + +.picomplete-items div { + padding: 3px 10px 3px 10px; + cursor: pointer; + background-color: #fff; + border-bottom: 1px solid #d4d4d4; +} + +.picomplete-items div:hover { + background-color: #e9e9e9; +} + +.picomplete-items::-webkit-scrollbar { + width: 2px; +} + +.picomplete-items::-webkit-scrollbar-track { + background: whitesmoke; +} + +.picomplete-items::-webkit-scrollbar-thumb { + background: grey; +} diff --git a/resources/js/component/from_select.js b/resources/js/component/from_select.js new file mode 100644 index 0000000..6acd453 --- /dev/null +++ b/resources/js/component/from_select.js @@ -0,0 +1,19 @@ +import PickleComplate from "../vendor/picomplete/picomplete"; + +(function () { + new PickleComplate({ + request: { + url: '/programs/construct/append/from_list?', + type: 'GET', + value: 'from', + text: 'from', + }, + config: { + type: 'server', + target: '#from_select', + clickCallback: (target, node) => { + target.value = node.value; + }, + }, + }) +})() diff --git a/resources/js/vendor/picomplete/picomplete.js b/resources/js/vendor/picomplete/picomplete.js new file mode 100644 index 0000000..4b14e98 --- /dev/null +++ b/resources/js/vendor/picomplete/picomplete.js @@ -0,0 +1,186 @@ +export default class PickleComplate { + /** + * + * @param {object} obj as tree object + */ + constructor(obj = null) { + //set config + this.config = obj.config; + //set fetch parameters + this.req_params = obj.request; + //target element + this.element = null; + //static data + this.container = obj.data; + //list div element + this.sug_div = null; + + if(this.config.reqCallback === undefined) this.config.reqCallback = null; + if(this.config.changeCallback === undefined) this.config.changeCallback = null; + + + //start events + this.staticEvents(); + } + + + /** + * this method will set events + */ + staticEvents() { + // key up event + document.querySelectorAll(this.config.target+' input').forEach(e => { + e.addEventListener('keyup', el => { + if(this.config.changeCallback !== undefined && this.config.changeCallback!== null) this.config.changeCallback(el.target); + if(el.target.value.trim().length>0){ + this.element = el.target; + this.closeAllLists(); + //if anything is match show list + setTimeout(() => { + this.getSuggests(el.target); + }, 30); + }else{ + this.closeAllLists(); + } + + if(this.config.changeCallback != null) this.config.changeCallback(el,el.target.value); + }); + }); + + //close all lists + document.addEventListener("click", e => { + //close all lists + this.closeAllLists(); + }); + + //listen item clicks + document.querySelectorAll(this.config.target).forEach(e => { + e.addEventListener('click', el => { + if(el.target.classList.contains('picomplete-item')){ + if(this.config.clickCallback !== undefined && this.config.clickCallback !== null){ + //send target element and node data + this.config.clickCallback(this.element,this.container[el.target.getAttribute('data-index')]) + } + } + }); + }); + } + + + + + //#region event transactions + /** + * this method will close all lists + */ + async closeAllLists() { + /*close all autocomplete lists in the document*/ + let elms = document.querySelectorAll('.picomplete-items'); + for (let i = 0; i < elms.length; i++) { + elms[i].parentNode.removeChild(elms[i]); + } + this.sug_div = null; + } + + + + async getSuggests(el) { + + //check container type + if(this.config.type === 'server'){ + await this.getData(el.value.toLowerCase()); + } + //for each item in container + if(this.container.length > 0){ + //first create div element for suggest + this.sug_div = document.createElement('DIV'); + this.sug_div.classList.add('picomplete-items'); + for (let i = 0; i < this.container.length; i++) { + if (this.container[i].text.toLowerCase().includes(el.value.toLowerCase()) || this.container[i].value.toLowerCase().includes(el.value.toLowerCase())) { + //create list item + let item = document.createElement('DIV'); + //set class + item.classList.add('picomplete-item'); + //set value + item.setAttribute('data-value',this.container[i].value); + //set value + item.setAttribute('data-index',i); + //set text + item.innerHTML = this.container[i].text; + //add item to list + this.sug_div.appendChild(item); + } + } + } + + //add list to input + if(this.sug_div !== null) this.element.parentNode.appendChild(this.sug_div); + } + + + + /** + * this method will send request to given parameters and return list of results + * @param {string} value + */ + async getData(value){ + //define if parameters is not defined + if(this.req_params.params===undefined){ + this.req_params.params = {}; + } + //set value to params + this.req_params.params.value = value; + await this.request({ + method: this.req_params.type, + url: this.req_params.url, + data:this.req_params.params + },this.config.reqCallback).then(rsp => { + this.container = []; + if(rsp.length>0){ + for (let i = 0; i <rsp.length; i++) { + this.container.push({ + value : rsp[i][this.req_params.value], + text : rsp[i][this.req_params.text], + }); + } + } + }); + } + //#endregion + + + + /** + * system request method + * @param {json object} rqs + */ + async request(rqs, reqCallback = null) { + let fD = new FormData(); + let rsp; + const url_params = []; + const op = { + method: rqs['method'], + }; + + if(reqCallback !== null) rqs['data'] = reqCallback(rqs['data']); + + if (rqs['method'] !== 'GET') { + op.body = fD; + for (let key in rqs['data']) { + fD.append(key, rqs['data'][key]); + } + }else{ + if(!rqs['url'].includes("&")){ + rqs['url']+='&'; + } + for (let key in rqs['data']) { + url_params.push(key+'='+rqs['data'][key]); + } + rqs['url']+=url_params.join('&'); + } + + rsp = await fetch(rqs['url'], op); + + return await rsp.json() + } +} diff --git a/resources/views/program/construct/append/create.blade.php b/resources/views/program/construct/append/create.blade.php index b9279d8..0943134 100644 --- a/resources/views/program/construct/append/create.blade.php +++ b/resources/views/program/construct/append/create.blade.php @@ -4,6 +4,8 @@ <title>录播节目点播信息修改</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link href="{{ mix('/css/app.css') }}" rel="stylesheet"/> + <script src="{{ mix('/js/manifest.js') }}" rel="script"></script> + <link href="{{ asset('/css/vendor/picomplete/picomplete.css') }}" rel="stylesheet"/> </head> <body> @include("common.header") @@ -28,7 +30,7 @@ 追加内容(点播可不填) <input class="form-input border-0 border-b-2 w-full" type="text" name="name" value="{{ old('name', $append->name) }}"> </label> - <label class="block my-2"> + <label class="block my-2" id="from_select"> 点播老板 <input class="form-input border-0 border-b-2 w-full" type="text" name="from" value="{{ old('from', $append->from) }}"> </label> @@ -66,4 +68,5 @@ </form> @include("common.footer") </body> +<script src="{{ mix('/js/component/from_select.js') }}" rel="script"></script> </html> diff --git a/routes/web.php b/routes/web.php index f783c89..c8fd0fe 100644 --- a/routes/web.php +++ b/routes/web.php @@ -55,6 +55,7 @@ Route::prefix("/programs/construct")->middleware("auth:web")->group(function (Ro $router->get('/{program}/append', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","index"])->name("program.construct.append.list"); $router->get('/{program}/append/add', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","add"])->name("program.construct.append.add"); $router->post('/{program}/append/add', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","create"])->name("program.construct.append.create"); + $router->get('/append/from_list', ["\\App\\Http\\Controllers\\ProgramAppendConstructController", "from_list"])->name("program.construct.append.from_list"); $router->get('/append/{append}', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","edit"])->name("program.construct.append.edit"); $router->post('/append/{append}', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","submit"])->name("program.construct.append.submit"); }); diff --git a/webpack.mix.js b/webpack.mix.js index 811c11b..bb80a03 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -11,14 +11,19 @@ const mix = require('laravel-mix'); | */ -mix.js('resources/js/app.js', 'public/js') +mix + .js('resources/js/component/from_select.js', 'public/js/component') + .js('resources/js/app.js', 'public/js') .extract(['axios', 'lodash']) .js('resources/js/webauthn.js', 'public/js') + .copy('resources/css/vendor', 'public/css/vendor') .postCss('resources/css/app.css', 'public/css', [ require('tailwindcss') ]) .disableNotifications(); if (mix.inProduction()) { mix.version(); +} else { + mix.sourceMaps() }