简易输入提示

This commit is contained in:
Jerry Yan 2022-08-08 14:19:08 +08:00
parent 5f50dc8f2e
commit 9609aef740
8 changed files with 265 additions and 2 deletions

View File

@ -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();
}
} }

View File

@ -1 +1,3 @@
*.js *.js
*.js.LICENSE.txt

View File

@ -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;
}

View File

@ -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;
},
},
})
})()

View File

@ -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()
}
}

View File

@ -4,6 +4,8 @@
<title>录播节目点播信息修改</title> <title>录播节目点播信息修改</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="{{ mix('/css/app.css') }}" rel="stylesheet"/> <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> </head>
<body> <body>
@include("common.header") @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) }}"> <input class="form-input border-0 border-b-2 w-full" type="text" name="name" value="{{ old('name', $append->name) }}">
</label> </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) }}"> <input class="form-input border-0 border-b-2 w-full" type="text" name="from" value="{{ old('from', $append->from) }}">
</label> </label>
@ -66,4 +68,5 @@
</form> </form>
@include("common.footer") @include("common.footer")
</body> </body>
<script src="{{ mix('/js/component/from_select.js') }}" rel="script"></script>
</html> </html>

View File

@ -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', ["\\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->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->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->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"); $router->post('/append/{append}', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","submit"])->name("program.construct.append.submit");
}); });

View File

@ -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']) .extract(['axios', 'lodash'])
.js('resources/js/webauthn.js', 'public/js') .js('resources/js/webauthn.js', 'public/js')
.copy('resources/css/vendor', 'public/css/vendor')
.postCss('resources/css/app.css', 'public/css', [ .postCss('resources/css/app.css', 'public/css', [
require('tailwindcss') require('tailwindcss')
]) ])
.disableNotifications(); .disableNotifications();
if (mix.inProduction()) { if (mix.inProduction()) {
mix.version(); mix.version();
} else {
mix.sourceMaps()
} }