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