├── .gitattributes ├── .gitignore ├── README.md ├── backend ├── LICENSE ├── README.md ├── TsAdm.Backend.sln ├── TsAdm.Core │ ├── Extensions │ │ ├── PredicateBuilder.cs │ │ └── QuerableExtension.cs │ ├── IPagedList.cs │ ├── PagedList.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── TsAdm.Core.csproj ├── TsAdm.Dashboard │ ├── App_Start │ │ ├── ApiException.cs │ │ ├── CustomAuthorizeAttribute.cs │ │ ├── DxExceptionAttribute.cs │ │ ├── GlobalExceptionHandler.cs │ │ └── WebApiConfig.cs │ ├── Controllers │ │ └── UserController.cs │ ├── Files │ │ └── Data │ │ │ └── grid.txt │ ├── Global.asax │ ├── Global.asax.cs │ ├── Models │ │ ├── DataSource.cs │ │ └── SearchCommand │ │ │ ├── BaseCommand.cs │ │ │ └── UserCommand.cs │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ └── PublishProfiles │ │ │ └── FolderProfile.pubxml │ ├── TsAdm.Dashboard.csproj │ ├── Util │ │ └── TsResponse │ │ │ ├── ResponseModel.cs │ │ │ └── TxResponseExtensions.cs │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ ├── index.html │ └── packages.config ├── TsAdm.Domain │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── TsAdm.Domain.csproj │ └── User.cs └── TsAdm.Repository │ ├── IRepository.cs │ ├── IUserRepository.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── TsAdm.Repository.csproj │ ├── UserRepository.cs │ └── packages.config └── frontend └── tsadmin ├── .babelrc ├── .gitignore ├── README.md ├── app ├── assets │ ├── css │ │ ├── element-ui-index.css │ │ ├── fonts │ │ │ ├── element-icons.eot │ │ │ ├── element-icons.svg │ │ │ ├── element-icons.ttf │ │ │ ├── element-icons.woff │ │ │ ├── icomoon.eot │ │ │ ├── icomoon.svg │ │ │ ├── icomoon.ttf │ │ │ ├── icomoon.woff │ │ │ ├── icons.eot │ │ │ ├── icons.svg │ │ │ ├── icons.ttf │ │ │ ├── icons.woff │ │ │ ├── icons.woff2 │ │ │ └── style.css │ │ ├── icon.min.css │ │ ├── main.css │ │ ├── semantic-ui-icon.css │ │ └── vue-tabpanel.css │ └── images │ │ ├── Axure-Components.svg │ │ ├── Module.svg │ │ ├── Sketch-Template.svg │ │ ├── banner-bg.svg │ │ ├── component.png │ │ ├── consistency.png │ │ ├── controllability.png │ │ ├── efficiency.png │ │ ├── element-demo.jpeg │ │ ├── feedback.png │ │ ├── guide.png │ │ ├── hamburger.png │ │ ├── logo.png │ │ ├── navbar_0.png │ │ ├── navbar_1.png │ │ ├── navbar_2.png │ │ ├── navbar_3.png │ │ ├── qrcode.png │ │ ├── resource.png │ │ └── stars.png ├── components │ ├── menu-item.vue │ ├── sidebar-menu.vue │ └── toolstrip.vue ├── config │ ├── config.js │ └── tabs.js ├── data │ ├── grid.txt │ └── menu.js ├── pages │ ├── dashboard │ │ ├── app.html │ │ ├── app.js │ │ └── app.vue │ └── login │ │ ├── app.html │ │ ├── app.js │ │ └── app.vue ├── util │ ├── http.js │ └── util.js ├── views │ ├── dashboard.vue │ ├── homepage.vue │ └── user.vue └── vuex │ └── store.js ├── package.json ├── server.js ├── webpack.config.js └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TsAdmin后台管理系统模板 2 | 3 | ### 前言 4 | 5 | 很高兴今天在这里跟大家分享一款全新的后台管理系统UI模板--TsAdmin. 6 | 7 | * * * 8 | 9 | **TsAdmin**是一款基于ASP.NET Web Api 2 + Vue.js + Element UI 的单页无刷新(无iframe)多选项卡的后台管理系统模板,截图如下 : 10 | 11 | ![基于Vue.js+Element UI的单页无刷新(无iframe)多选项卡的后台管理系统模板--TsAdmin](https://statics.codedefault.com/uploads/2017/03/tsadmin-vue-element-ui-demo.png) 12 | 13 | 动图功能展示: 14 | 15 | ![基于Vue.js+Element UI的单页无刷新(无iframe)多选项卡的后台管理系统模板--TsAdmin](https://statics.codedefault.com/uploads/2018/01/tmp/vuejs-element-ui-single-page-application-spa-web-desktop-appliction-admin-template-demo-preview-001.gif) 16 | 17 | TsAdmin中主要集成了以下的优秀功能: 18 | 19 | ### 一、 无限递归的左侧菜单 20 | 21 | 22 | 支持无限级递归的菜单树,你只需要提供格式规范的JSON数组对象即可,如: 23 | 24 | ``` 25 | [ 26 | { 27 | "title": "系统设置", 28 | "name": "1*system*settings", 29 | "expand": true, 30 | "uniqueNo": "1*systemsettings", 31 | "children": [ 32 | { 33 | "title": "首页", 34 | "name": "首页", 35 | "path": "/homepage", 36 | "allowClose": false, 37 | "uniqueNo": "homepage", 38 | "children": [] 39 | }, 40 | { 41 | "title": "仪表盘", 42 | "name": "仪表盘", 43 | "path": "/dashboard", 44 | "uniqueNo": "dashboard", 45 | "allowClose": true, 46 | "children": [] 47 | }, 48 | { 49 | "title": "用户管理[未实现]", 50 | "name": "10001user*management", 51 | "uniqueNo": "10001*user*management", 52 | "children": [] 53 | }, 54 | { 55 | "title": "角色管理[未实现]", 56 | "name": "10002*rolemanagement", 57 | "uniqueNo": "10002rolemanagement", 58 | "children": [] 59 | }, 60 | { 61 | "title": "权限管理[未实现]", 62 | "name": "10003permissionmanagement", 63 | "uniqueNo": "10003permissionmanagement", 64 | "children": [] 65 | } 66 | ] 67 | }, 68 | { 69 | "title": "报表设置", 70 | "name": "2report*settings", 71 | "uniqueNo": "2*report*settings", 72 | "expand": false, 73 | "children": [ 74 | { 75 | "title": "表格报表管理[未实现]", 76 | "name": "20001*gridreportmanagement", 77 | "uniqueNo": "20001*grid*report*management", 78 | "children": [] 79 | }, 80 | { 81 | "title": "图形报表管理[未实现]", 82 | "name": "20002*graphyreportmanagement", 83 | "uniqueNo": "20002*graphy*report*management", 84 | "children": [] 85 | } 86 | ] 87 | } 88 | ] 89 | ``` 90 | 91 | ### 二、可展开/收缩的左侧菜单 92 | 93 | 94 | TsAdmin实现了点击头部导航的"菜单开关"来切换菜单的展开/收缩状态,并带炫酷的动画效果 95 | 96 | ### 三、整个页面无刷新(且以无iframe实现) 97 | 98 | 这也是TsAdmin后台模板系统框架比目前流行的其他框架更舒服的地方吧,这也是作者一直在追求和努力实现的后台管理系统的一种方式。 99 | 100 | 在TsAdmin模板中,你将可以体验中SPA的流畅操作以及更好的交互体验,因为TsAdmin是页面无刷新的,也无iframe嵌套的。 101 | 102 | ### 四、以选项卡方式打开各个菜单对应的窗体 103 | 104 | 在TsAdmin中,你将体验到类似桌面应用的选项卡操作体验,每个菜单选项都可以以选项卡的方式在右侧的选项卡显示区域呈现。 105 | 106 | 更高级的是,TsAdmin还支持将当前打开的选项卡添加到头部的工作台,当把选项卡成功添加到工作台后,便可以从工作台中重新打开或者激活对应的窗体。 107 | 108 | ### 五、数据双向绑定及前后端分离 109 | 110 | 数据双向绑定这是肯定的,因为TsAdmin是基于Vue.js的,至于前后端分离,目前预览版的数据还是纯文本的,所以项目中也提供了一个基于ASP.NET Web Api 2的示例项目(项目中backend目录中),你可以将文本数据替换成API请求。 111 | 112 | 欢迎大家对TsAdmin踊跃吐槽。同时也欢迎加入码友网的QQ群:483350228。 113 | 114 | > **注:**此模板适合于有一定VUE基础的童鞋 115 | 116 | ### 六、安装及预览 117 | 118 | #### 安装 119 | 120 | 1. 将项目使用git命令行工具克隆到本地: 121 | > git clone https://github.com/codedefault/tsadmin.git 122 | 123 | 2. 在命令行工具中,进入目录:/frontend/tsadmin/ ,运行如下命令以还原前端项目所依赖的各种js包: 124 | > npm install 125 | 126 | 3. 完成步骤2后,在命令行中运行如下命令以启动前端项目的服务: 127 | > npm run dev 128 | 129 | 4. 在Visual Studio 2015 或者以上版本中打开后端解决方案 **TsAdm.Backend.sln** (位于目录:/backend/TsAdm.Backend.sln),运行项目:TsAdm.Dashboard; 130 | 131 | 5. 完成步骤4后,确保后端项目和前端调用地址一致后,在浏览器中打开如下地址即可预览**TsAdmin**后台管理系统模板: 132 | 133 | > http://localhost:8860/dashboard.html 134 | 135 | 136 | #### 在线预览地址 137 | 138 | > http://demo.codedefault.com/demo/vue/tsadm/dashboard.html 139 | -------------------------------------------------------------------------------- /backend/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # tsadmin 2 | asp.net web api 2 + vue.js +elementui 实现的前/后端分离的后台管理系统示例程序。 3 | -------------------------------------------------------------------------------- /backend/TsAdm.Backend.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26403.7 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TsAdm.Dashboard", "TsAdm.Dashboard\TsAdm.Dashboard.csproj", "{460986A5-103D-4B0F-AC97-2974DB340F73}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TsAdm.Repository", "TsAdm.Repository\TsAdm.Repository.csproj", "{D5AF25EC-E485-4D55-A1D5-428BE1AB22F2}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TsAdm.Domain", "TsAdm.Domain\TsAdm.Domain.csproj", "{99F01900-5483-4D01-8DDD-9513283D9D3A}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TsAdm.Core", "TsAdm.Core\TsAdm.Core.csproj", "{A1B6FE47-5980-47E0-B109-AC0BDEEC40AD}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {460986A5-103D-4B0F-AC97-2974DB340F73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {460986A5-103D-4B0F-AC97-2974DB340F73}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {460986A5-103D-4B0F-AC97-2974DB340F73}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {460986A5-103D-4B0F-AC97-2974DB340F73}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {D5AF25EC-E485-4D55-A1D5-428BE1AB22F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {D5AF25EC-E485-4D55-A1D5-428BE1AB22F2}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {D5AF25EC-E485-4D55-A1D5-428BE1AB22F2}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {D5AF25EC-E485-4D55-A1D5-428BE1AB22F2}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {99F01900-5483-4D01-8DDD-9513283D9D3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {99F01900-5483-4D01-8DDD-9513283D9D3A}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {99F01900-5483-4D01-8DDD-9513283D9D3A}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {99F01900-5483-4D01-8DDD-9513283D9D3A}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {A1B6FE47-5980-47E0-B109-AC0BDEEC40AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {A1B6FE47-5980-47E0-B109-AC0BDEEC40AD}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {A1B6FE47-5980-47E0-B109-AC0BDEEC40AD}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {A1B6FE47-5980-47E0-B109-AC0BDEEC40AD}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /backend/TsAdm.Core/Extensions/PredicateBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace TsAdm.Core.Extensions 7 | { 8 | public static class PredicateBuilder 9 | { 10 | /// 11 | /// Creates a predicate that evaluates to true. 12 | /// 13 | public static Expression> True() { return param => true; } 14 | 15 | /// 16 | /// Creates a predicate that evaluates to false. 17 | /// 18 | public static Expression> False() { return param => false; } 19 | 20 | /// 21 | /// Creates a predicate expression from the specified lambda expression. 22 | /// 23 | public static Expression> Create(Expression> predicate) { return predicate; } 24 | 25 | /// 26 | /// Combines the first predicate with the second using the logical "and". 27 | /// 28 | public static Expression> And(this Expression> first, Expression> second) 29 | { 30 | if (first == null) 31 | { 32 | return Create(second); 33 | } 34 | return first.Compose(second, Expression.AndAlso); 35 | } 36 | 37 | /// 38 | /// Combines the first predicate with the second using the logical "or". 39 | /// 40 | public static Expression> Or(this Expression> first, Expression> second) 41 | { 42 | if (first == null) 43 | { 44 | return Create(second); 45 | } 46 | return first.Compose(second, Expression.OrElse); 47 | } 48 | 49 | /// 50 | /// Negates the predicate. 51 | /// 52 | public static Expression> Not(this Expression> expression) 53 | { 54 | var negated = Expression.Not(expression.Body); 55 | return Expression.Lambda>(negated, expression.Parameters); 56 | } 57 | 58 | /// 59 | /// Combines the first expression with the second using the specified merge function. 60 | /// 61 | static Expression Compose(this Expression first, Expression second, Func merge) 62 | { 63 | // zip parameters (map from parameters of second to parameters of first) 64 | var map = first.Parameters 65 | .Select((f, i) => new { f, s = second.Parameters[i] }) 66 | .ToDictionary(p => p.s, p => p.f); 67 | 68 | // replace parameters in the second lambda expression with the parameters in the first 69 | var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); 70 | 71 | // create a merged lambda expression with parameters from the first expression 72 | return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); 73 | } 74 | 75 | class ParameterRebinder : ExpressionVisitor 76 | { 77 | readonly Dictionary map; 78 | 79 | ParameterRebinder(Dictionary map) 80 | { 81 | this.map = map ?? new Dictionary(); 82 | } 83 | 84 | public static Expression ReplaceParameters(Dictionary map, Expression exp) 85 | { 86 | return new ParameterRebinder(map).Visit(exp); 87 | } 88 | 89 | protected override Expression VisitParameter(ParameterExpression p) 90 | { 91 | ParameterExpression replacement; 92 | 93 | if (map.TryGetValue(p, out replacement)) 94 | { 95 | p = replacement; 96 | } 97 | 98 | return base.VisitParameter(p); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /backend/TsAdm.Core/Extensions/QuerableExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | 6 | namespace TsAdm.Core.Extensions 7 | { 8 | public static class QuerableExtension 9 | { 10 | public static IQueryable OrderBy(this IQueryable source, string orderByProperty, bool desc = false) 11 | { 12 | var command = desc ? "OrderByDescending" : "OrderBy"; 13 | var type = typeof(TEntity); 14 | var property = type.GetProperty(orderByProperty, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); 15 | var parameter = Expression.Parameter(type, "p"); 16 | var propertyAccess = Expression.MakeMemberAccess(parameter, property); 17 | var orderByExpression = Expression.Lambda(propertyAccess, parameter); 18 | var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExpression)); 19 | return source.Provider.CreateQuery(resultExpression); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /backend/TsAdm.Core/IPagedList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace TsAdm.Core 4 | { 5 | public interface IPagedList : IList 6 | { 7 | int PageIndex { get; } 8 | int PageSize { get; } 9 | int TotalCount { get; } 10 | int TotalPages { get; } 11 | bool HasPreviousPage { get; } 12 | bool HasNextPage { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/TsAdm.Core/PagedList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TsAdm.Core 6 | { 7 | /// 8 | /// 分页组件实体类 9 | /// 10 | /// 泛型实体 11 | 12 | [Serializable] 13 | public class PagedList : List, IPagedList 14 | { 15 | /// 16 | /// 构造函数 17 | /// 18 | /// 数据源 19 | /// 分页索引 20 | /// 分页大小 21 | public PagedList(IQueryable source, int pageIndex, int pageSize) 22 | { 23 | var total = source.Count(); 24 | TotalCount = total; 25 | TotalPages = total / pageSize; 26 | 27 | if (total % pageSize > 0) 28 | TotalPages++; 29 | 30 | PageSize = pageSize; 31 | PageIndex = pageIndex; 32 | 33 | AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList()); 34 | } 35 | 36 | /// 37 | /// 构造函数 38 | /// 39 | /// 数据源 40 | /// 分页索引 41 | /// 分页大小 42 | public PagedList(IList source, int pageIndex, int pageSize) 43 | { 44 | TotalCount = source.Count(); 45 | TotalPages = TotalCount / pageSize; 46 | 47 | if (TotalCount % pageSize > 0) 48 | TotalPages++; 49 | 50 | PageSize = pageSize; 51 | PageIndex = pageIndex; 52 | AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList()); 53 | } 54 | 55 | /// 56 | /// 构造函数 57 | /// 58 | /// 数据源 59 | /// 分页索引 60 | /// 分页大小 61 | /// 总记录数 62 | public PagedList(IEnumerable source, int pageIndex, int pageSize, int totalCount) 63 | { 64 | //pageSize = pageSize <= 0 ? 10 : pageSize; 65 | TotalCount = totalCount; 66 | TotalPages = TotalCount / pageSize; 67 | 68 | if (TotalCount % pageSize > 0) 69 | TotalPages++; 70 | 71 | PageSize = pageSize; 72 | PageIndex = pageIndex; 73 | AddRange(source); 74 | } 75 | 76 | /// 77 | /// 分页索引 78 | /// 79 | public int PageIndex { get; } 80 | /// 81 | /// 分页大小 82 | /// 83 | public int PageSize { get; private set; } 84 | /// 85 | /// 总记录数 86 | /// 87 | public int TotalCount { get; } 88 | /// 89 | /// 总页数 90 | /// 91 | public int TotalPages { get; } 92 | 93 | /// 94 | /// 是否有上一页 95 | /// 96 | public bool HasPreviousPage 97 | { 98 | get { return (PageIndex > 0); } 99 | } 100 | /// 101 | /// 是否有下一页 102 | /// 103 | public bool HasNextPage 104 | { 105 | get { return (PageIndex + 1 < TotalPages); } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /backend/TsAdm.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TsAdm.Core")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TsAdm.Core")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a1b6fe47-5980-47e0-b109-ac0bdeec40ad")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /backend/TsAdm.Core/TsAdm.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A1B6FE47-5980-47E0-B109-AC0BDEEC40AD} 8 | Library 9 | Properties 10 | TsAdm.Core 11 | TsAdm.Core 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/App_Start/ApiException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace TsAdm.Dashboard 5 | { 6 | public class ApiException : Exception 7 | { 8 | public ApiException(HttpStatusCode statusCode, string message, Exception ex) 9 | : base(message, ex) 10 | { 11 | StatusCode = statusCode; 12 | } 13 | 14 | public ApiException(HttpStatusCode statusCode, string message) 15 | : base(message) 16 | { 17 | StatusCode = statusCode; 18 | } 19 | 20 | public ApiException(HttpStatusCode statusCode) 21 | { 22 | StatusCode = statusCode; 23 | } 24 | 25 | public HttpStatusCode StatusCode { get; } 26 | } 27 | } -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/App_Start/CustomAuthorizeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http; 3 | using System.Security.Claims; 4 | using System.Web.Http; 5 | using System.Web.Http.Controllers; 6 | 7 | namespace TsAdm.Dashboard 8 | { 9 | public class CustomAuthorizeAttribute : AuthorizeAttribute 10 | { 11 | public override void OnAuthorization(HttpActionContext actionContext) 12 | { 13 | var principal = actionContext.RequestContext.Principal as ClaimsPrincipal; 14 | 15 | if (principal != null && !principal.Identity.IsAuthenticated) 16 | { 17 | actionContext.Response = new HttpResponseMessage (HttpStatusCode.Forbidden); 18 | return; 19 | } 20 | 21 | //var userName = principal.FindFirst(ClaimTypes.Name).Value; 22 | //var newCustomClaim = principal.FindFirst("newCustomClaim").Value; 23 | //var roles = Roles; 24 | 25 | base.OnAuthorization(actionContext); 26 | } 27 | 28 | } 29 | } -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/App_Start/DxExceptionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http; 3 | using System.Web.Http; 4 | using System.Web.Http.Filters; 5 | 6 | namespace TsAdm.Dashboard 7 | { 8 | public class DxExceptionAttribute :ExceptionFilterAttribute 9 | { 10 | public override void OnException(HttpActionExecutedContext actionExecutedContext) 11 | { 12 | var exception = actionExecutedContext.Exception as ApiException; 13 | if (exception != null) 14 | { 15 | actionExecutedContext.Response = actionExecutedContext.Request.CreateErrorResponse( 16 | exception.StatusCode, exception.Message); 17 | } 18 | else 19 | { 20 | actionExecutedContext.Response =actionExecutedContext.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, 21 | new HttpError(actionExecutedContext.Exception.Message)); 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/App_Start/GlobalExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http; 3 | using System.Web.Http.ExceptionHandling; 4 | using System.Web.Http.Results; 5 | 6 | namespace TsAdm.Dashboard 7 | { 8 | public class GlobalExceptionHandler : ExceptionHandler 9 | { 10 | public override void Handle(ExceptionHandlerContext context) 11 | { 12 | const string errorMessage = "An unexpected error occured"; 13 | var response = context.Request.CreateResponse(HttpStatusCode.InternalServerError, 14 | new 15 | { 16 | Message = errorMessage 17 | }); 18 | response.Headers.Add("X-Error", errorMessage); 19 | context.Result = new ResponseMessageResult(response); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Net.Http.Formatting; 3 | using System.Net.Http.Headers; 4 | using System.Web.Http; 5 | using System.Web.Http.Cors; 6 | using System.Web.Http.ExceptionHandling; 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Serialization; 9 | 10 | namespace TsAdm.Dashboard 11 | { 12 | public static class WebApiConfig 13 | { 14 | public static void Register(HttpConfiguration config) 15 | { 16 | // Web API 配置和服务 17 | EnableCrossSiteRequests(config); 18 | JsonResposeFormatter(config); 19 | config.MapHttpAttributeRoutes(); 20 | config.Filters.Add(new DxExceptionAttribute()); 21 | 22 | config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler()); 23 | 24 | config.Routes.MapHttpRoute( 25 | "DefaultApi", 26 | "api/{controller}/{action}/{id}", 27 | new { id = RouteParameter.Optional }); 28 | } 29 | 30 | private static void JsonResposeFormatter(HttpConfiguration config) 31 | { 32 | config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); 33 | var jsonFormatter = config.Formatters.OfType().First(); 34 | jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 35 | config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false; 36 | 37 | var settings = jsonFormatter.SerializerSettings; 38 | settings.Formatting = Formatting.Indented; 39 | settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 40 | } 41 | private static void EnableCrossSiteRequests(HttpConfiguration config) 42 | { 43 | var cors = new EnableCorsAttribute("*", "*", "*"); 44 | config.EnableCors(cors); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Controllers/UserController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Web.Http; 5 | using TsAdm.Core.Extensions; 6 | using TsAdm.Dashboard.Models; 7 | using TsAdm.Dashboard.Models.SearchCommand; 8 | using TsAdm.Dashboard.Util.TsResponse; 9 | using TsAdm.Domain; 10 | using TsAdm.Repository; 11 | 12 | namespace TsAdm.Dashboard.Controllers 13 | { 14 | public class UserController : ApiController 15 | { 16 | private readonly IUserRepository _userRepository = new UserRepository(); 17 | [HttpGet] 18 | public IHttpActionResult Get() 19 | { 20 | return Ok(new { UserName = "Admin", EmailAddress = "master@admin.com" }); 21 | } 22 | 23 | [HttpGet] 24 | public IHttpActionResult Find(int id) 25 | { 26 | Thread.Sleep(new Random().Next(500, 1500)); 27 | var model = _userRepository.FindById(id); 28 | return Ok(new { data = model }); 29 | } 30 | 31 | [HttpPost] 32 | public IHttpActionResult Grid(UserCommand command) 33 | { 34 | Thread.Sleep(new Random().Next(500, 1500)); 35 | var response = TxResponseExtensions.GetTxResponseModel; 36 | var predicateBuilder = PredicateBuilder.Create(null); 37 | if (!string.IsNullOrEmpty(command.FieldValue)) 38 | { 39 | switch (command.FieldName) 40 | { 41 | case "first_name": 42 | predicateBuilder = predicateBuilder.And(x => x.first_name.IndexOf(command.FieldValue.ToString(),StringComparison.CurrentCultureIgnoreCase)>=0); 43 | break; 44 | case "last_name": 45 | predicateBuilder = predicateBuilder.And(x => x.last_name.IndexOf(command.FieldValue.ToString(), StringComparison.CurrentCultureIgnoreCase) >= 0); 46 | break; 47 | case "email": 48 | predicateBuilder = predicateBuilder.And(x => x.email.IndexOf(command.FieldValue.ToString(), StringComparison.CurrentCultureIgnoreCase) >= 0); 49 | break; 50 | } 51 | } 52 | if (!string.Equals(command.Gender, "All", StringComparison.InvariantCultureIgnoreCase)) 53 | { 54 | predicateBuilder = predicateBuilder.And(x => x.gender == command.Gender); 55 | } 56 | var lst = _userRepository.FindPagedList(predicateBuilder, command.Sort.Prop, command.Sort.Desc, command.Pagination.CurrentPage - 1, command.Pagination.PageSize); 57 | var grid = new DataSource(lst) 58 | { 59 | Data = lst 60 | }; 61 | return Ok(new { response, grid }); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="TsAdm.Dashboard.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | 3 | namespace TsAdm.Dashboard 4 | { 5 | public class WebApiApplication : System.Web.HttpApplication 6 | { 7 | protected void Application_Start() 8 | { 9 | GlobalConfiguration.Configure(WebApiConfig.Register); 10 | } 11 | 12 | protected void Application_PreSendRequestHeaders() 13 | { 14 | Response.Headers.Set("Server", "Apache"); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Models/DataSource.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using TsAdm.Core; 3 | using TsAdm.Dashboard.Models.SearchCommand; 4 | 5 | namespace TsAdm.Dashboard.Models 6 | { 7 | public class DataSource 8 | { 9 | public DataSource(IPagedList list) 10 | { 11 | Pagination = new Pagination 12 | { 13 | TotalPages = list.TotalPages, 14 | Page = list.PageIndex + 1, 15 | CurrentPage=list.PageIndex+1, 16 | Total=list.TotalCount, 17 | PageSize = list.PageSize 18 | }; 19 | } 20 | public object ExtraData { get; set; } 21 | 22 | public IEnumerable Data { get; set; } 23 | 24 | public object Errors { get; set; } 25 | 26 | public Pagination Pagination { get; set; } 27 | } 28 | } -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Models/SearchCommand/BaseCommand.cs: -------------------------------------------------------------------------------- 1 | namespace TsAdm.Dashboard.Models.SearchCommand 2 | { 3 | public class BaseCommand 4 | { 5 | public BaseCommand() 6 | { 7 | Pagination = new Pagination(); 8 | Sort = new Sort(); 9 | } 10 | public Pagination Pagination { get; set; } 11 | public Sort Sort { get; set; } 12 | } 13 | public class Pagination 14 | { 15 | public int TotalPages { get; set; } 16 | public int Page { get; set; } 17 | 18 | /// 19 | /// 当前页 20 | /// 21 | public int CurrentPage { get; set; } 22 | /// 23 | /// 每页大小 24 | /// 25 | public int PageSize { get; set; } 26 | 27 | /// 28 | /// 每页显示 29 | /// 30 | public int[] PageSizes 31 | { 32 | get 33 | { 34 | return new[] { 5, 10, 15, 20, 25, 50, 100, 200, 300, 400, 500 }; 35 | } 36 | } 37 | /// 38 | /// 总记录数 39 | /// 40 | public int Total { get; set; } 41 | } 42 | 43 | public class Sort 44 | { 45 | public Sort() 46 | { 47 | Order = "ascending"; 48 | } 49 | public string Prop { get; set; } 50 | public string Order { get; set; } 51 | 52 | public bool Desc 53 | { 54 | get 55 | { 56 | return Order.ToLower() == "descending"; 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Models/SearchCommand/UserCommand.cs: -------------------------------------------------------------------------------- 1 | namespace TsAdm.Dashboard.Models.SearchCommand 2 | { 3 | public class UserCommand :BaseCommand 4 | { 5 | public string FieldName { get; set; } 6 | public string FieldValue { get; set; } 7 | public string Gender { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TsAdm.Dashboard")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TsAdm.Dashboard")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("460986a5-103d-4b0f-ac97-2974db340f73")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Properties/PublishProfiles/FolderProfile.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | FileSystem 9 | FileSystem 10 | Release 11 | Any CPU 12 | 13 | True 14 | False 15 | bin\Release\PublishOutput 16 | True 17 | 18 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/TsAdm.Dashboard.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 8 | 9 | 2.0 10 | {460986A5-103D-4B0F-AC97-2974DB340F73} 11 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 12 | Library 13 | Properties 14 | TsAdm.Dashboard 15 | TsAdm.Dashboard 16 | v4.5.2 17 | true 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | true 29 | full 30 | false 31 | bin\ 32 | DEBUG;TRACE 33 | prompt 34 | 4 35 | 36 | 37 | pdbonly 38 | true 39 | bin\ 40 | TRACE 41 | prompt 42 | 4 43 | 44 | 45 | 46 | 47 | ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll 48 | 49 | 50 | 51 | 52 | ..\packages\Microsoft.AspNet.Cors.5.2.3\lib\net45\System.Web.Cors.dll 53 | True 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | ..\packages\Microsoft.AspNet.WebApi.Cors.5.2.3\lib\net45\System.Web.Http.Cors.dll 64 | True 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 77 | 78 | 79 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 80 | 81 | 82 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | Global.asax 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | Web.config 113 | 114 | 115 | Web.config 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | {a1b6fe47-5980-47e0-b109-ac0bdeec40ad} 125 | TsAdm.Core 126 | 127 | 128 | {99f01900-5483-4d01-8ddd-9513283d9d3a} 129 | TsAdm.Domain 130 | 131 | 132 | {d5af25ec-e485-4d55-a1d5-428be1ab22f2} 133 | TsAdm.Repository 134 | 135 | 136 | 137 | 10.0 138 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | True 148 | True 149 | 12548 150 | / 151 | http://localhost:12548/ 152 | False 153 | False 154 | 155 | 156 | False 157 | 158 | 159 | 160 | 161 | 168 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Util/TsResponse/ResponseModel.cs: -------------------------------------------------------------------------------- 1 | namespace TsAdm.Dashboard.Util.TsResponse 2 | { 3 | /// 4 | /// 响应消息实体对象 5 | /// 6 | public class ResponseModel 7 | { 8 | public ResponseModel() 9 | { 10 | Extra = ""; 11 | Success = true; 12 | Message = ""; 13 | } 14 | /// 15 | /// 成功与否的标识 16 | /// 17 | public bool Success { get; set; } 18 | /// 19 | /// 消息 20 | /// 21 | public string Message { get; set; } 22 | /// 23 | /// 附加的数据(对象) 24 | /// 25 | public object Extra { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Util/TsResponse/TxResponseExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace TsAdm.Dashboard.Util.TsResponse 2 | { 3 | public static class TxResponseExtensions 4 | { 5 | public static ResponseModel GetTxResponseModel 6 | { 7 | get { return new ResponseModel(); } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Home Page 9 | 10 | 11 | -------------------------------------------------------------------------------- /backend/TsAdm.Dashboard/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /backend/TsAdm.Domain/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TsAdm.Domain")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TsAdm.Domain")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("99f01900-5483-4d01-8ddd-9513283d9d3a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /backend/TsAdm.Domain/TsAdm.Domain.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {99F01900-5483-4D01-8DDD-9513283D9D3A} 8 | Library 9 | Properties 10 | TsAdm.Domain 11 | TsAdm.Domain 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /backend/TsAdm.Domain/User.cs: -------------------------------------------------------------------------------- 1 | namespace TsAdm.Domain 2 | { 3 | public class User 4 | { 5 | public int Id { get; set; } 6 | public string first_name { get; set; } 7 | public string last_name { get; set; } 8 | public string email { get; set; } 9 | public string gender { get; set; } 10 | public string ip_address { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /backend/TsAdm.Repository/IRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using TsAdm.Core; 5 | 6 | namespace TsAdm.Repository 7 | { 8 | public interface IRepository where T :class 9 | { 10 | IEnumerable FindAll(); 11 | T FindById(int id); 12 | T Add(T entity); 13 | bool Update(T entity); 14 | bool Delete(int id); 15 | IPagedList FindPagedList(Expression> predicate, string order, bool desc, int pageIndex = 0, int pageSize = 20); 16 | } 17 | } -------------------------------------------------------------------------------- /backend/TsAdm.Repository/IUserRepository.cs: -------------------------------------------------------------------------------- 1 | using TsAdm.Domain; 2 | 3 | namespace TsAdm.Repository 4 | { 5 | public interface IUserRepository :IRepository 6 | { 7 | 8 | } 9 | } -------------------------------------------------------------------------------- /backend/TsAdm.Repository/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TsAdm.Repository")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TsAdm.Repository")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("d5af25ec-e485-4d55-a1d5-428be1ab22f2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /backend/TsAdm.Repository/TsAdm.Repository.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {D5AF25EC-E485-4D55-A1D5-428BE1AB22F2} 8 | Library 9 | Properties 10 | TsAdm.Repository 11 | TsAdm.Repository 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | {a1b6fe47-5980-47e0-b109-ac0bdeec40ad} 55 | TsAdm.Core 56 | 57 | 58 | {99f01900-5483-4d01-8ddd-9513283d9d3a} 59 | TsAdm.Domain 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /backend/TsAdm.Repository/UserRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Web.Hosting; 7 | using Newtonsoft.Json; 8 | using TsAdm.Core; 9 | using TsAdm.Core.Extensions; 10 | using TsAdm.Domain; 11 | 12 | namespace TsAdm.Repository 13 | { 14 | public class UserRepository : IUserRepository 15 | { 16 | private IQueryable _users= JsonConvert.DeserializeObject>(File.ReadAllText(HostingEnvironment.MapPath(@"~/Files/Data/grid.txt"))).AsQueryable(); 17 | public IEnumerable FindAll() 18 | { 19 | return _users; 20 | } 21 | 22 | public User FindById(int id) 23 | { 24 | return _users.FirstOrDefault(x => x.Id == id); 25 | } 26 | 27 | public User Add(User entity) 28 | { 29 | throw new NotImplementedException(); 30 | } 31 | 32 | public bool Update(User entity) 33 | { 34 | throw new NotImplementedException(); 35 | } 36 | 37 | public bool Delete(int id) 38 | { 39 | throw new NotImplementedException(); 40 | } 41 | 42 | public IPagedList FindPagedList(Expression> predicate, string order, bool desc, int pageIndex = 0, int pageSize = 20) 43 | { 44 | _users = predicate == null ? _users : _users.Where(predicate); 45 | var totalCount = _users.Count(); 46 | _users = _users.OrderBy(order, desc).Skip(pageIndex * pageSize).Take(pageSize); 47 | var items = new PagedList(_users, pageIndex, pageSize, totalCount); 48 | return items; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /backend/TsAdm.Repository/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/tsadmin/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /frontend/tsadmin/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | .idea 6 | -------------------------------------------------------------------------------- /frontend/tsadmin/README.md: -------------------------------------------------------------------------------- 1 | # vue-multiple-pages 2 | 3 | >Vue2.0多页应用 4 | 5 | ## Links 依赖链接 6 | 7 | 1. [Vue](https://github.com/vuejs/vue) 8 | 2. [Webpack](https://github.com/webpack/webpack) 9 | 3. [Element](https://github.com/ElemeFE/element) 10 | 11 | ## Start 开始 12 | 13 | - Clone or download this repository 14 | - Enter your local directory, and install dependencies: 15 | 16 | ``` bash 17 | npm install 18 | ``` 19 | 20 | ## Develop 开发构建 21 | 22 | ``` bash 23 | # serve with hot reload at localhost:8080 24 | npm run dev 25 | 26 | ``` 27 | 28 | [http://localhost:8010/user/login.html](http://localhost:8010/user/login.html) 29 | 30 | [http://localhost:8010/user/index.html](http://localhost:8010/user/index.html) 31 | 32 | ## Build 生产构建 33 | 34 | ``` bash 35 | # build for production with minification 36 | npm run build 37 | node server.js 38 | ``` 39 | 40 | [http://localhost:2333/user/login.html](http://localhost:2333/user/login.html) 41 | 42 | ## Folder Structure 文件目录 43 | 44 | ```bash 45 | ├── src # 主目录 46 | │ ├── assets # 资源目录 47 | │ │ ├── css # css 48 | │ │ └── img # 图片目录 49 | │ ├── components # 自定义组件目录 50 | │ └── pages # 页面目录 51 | │ └── user # 业务模块目录 52 | │ ├── index # index 页面 53 | │ │ ├── app.js # 入口js 54 | │ │ ├── app.html # html模板 55 | │ │ └── app.vue # index 页面组件 56 | │ └── login # login 页面 57 | │ ├── app.js # 入口js 58 | │ ├── app.html # html模板 59 | │ └── app.vue # login 页面组件 60 | ├── dist # npm run build生成的目录 61 | ├── node_modules # dependencies 62 | ├── .babelrc # babel文件 63 | ├── server.js # 用于查看npm run build的server.js,端口2333 64 | ├── webpack.config.js # webpack配置目录 65 | ├── node_modules # dependencies 66 | └── package.json # package info 67 | ``` 68 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/element-icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/css/fonts/element-icons.eot -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/element-icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Created by FontForge 20120731 at Tue Sep 6 12:16:07 2016 6 | By admin 7 | 8 | 9 | 10 | 24 | 26 | 28 | 30 | 32 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 57 | 60 | 63 | 66 | 69 | 72 | 75 | 77 | 79 | 83 | 85 | 87 | 90 | 93 | 96 | 99 | 101 | 104 | 107 | 109 | 112 | 117 | 121 | 125 | 128 | 131 | 134 | 138 | 142 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/css/fonts/element-icons.ttf -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/css/fonts/element-icons.woff -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/css/fonts/icomoon.eot -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/css/fonts/icomoon.ttf -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/css/fonts/icomoon.woff -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/css/fonts/icons.eot -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/css/fonts/icons.ttf -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/css/fonts/icons.woff -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/css/fonts/icons.woff2 -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/fonts/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'icomoon'; 3 | src: url('assets/styles/fonts/icomoon.eot?h6xgdm'); 4 | src: url('assets/styles/fonts/icomoon.eot?h6xgdm#iefix') format('embedded-opentype'), 5 | url('assets/styles/fonts/icomoon.ttf?h6xgdm') format('truetype'), 6 | url('assets/styles/fonts/icomoon.woff?h6xgdm') format('woff'), 7 | url('assets/styles/fonts/icomoon.svg?h6xgdm#icomoon') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | [class^="icon-"], [class*=" icon-"] { 13 | /* use !important to prevent issues with browser extensions that change fonts */ 14 | font-family: 'icomoon' !important; 15 | speak: none; 16 | font-style: normal; 17 | font-weight: normal; 18 | font-variant: normal; 19 | text-transform: none; 20 | line-height: 1; 21 | 22 | /* Better Font Rendering =========== */ 23 | -webkit-font-smoothing: antialiased; 24 | -moz-osx-font-smoothing: grayscale; 25 | } 26 | 27 | .icon-rate-face-off:before { 28 | content: "\e900"; 29 | } 30 | .icon-rate-face-1:before { 31 | content: "\e901"; 32 | } 33 | .icon-rate-face-2:before { 34 | content: "\e902"; 35 | } 36 | .icon-rate-face-3:before { 37 | content: "\e903"; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/main.css: -------------------------------------------------------------------------------- 1 | *{margin:0;padding:0;} 2 | *, *::after, *::before {box-sizing: border-box;} 3 | *, *::after, *::before, input[type="checkbox"], input[type="radio"] {box-sizing: border-box;} 4 | *, *::after, *::before {box-sizing: border-box;} 5 | *, *::after, *::before, input[type="checkbox"], input[type="radio"] {box-sizing: border-box;} 6 | html{height: 100%;} 7 | body {font:14px/1.5 "Microsoft YaHei";color:#333;word-wrap:break-word;word-break:break-all;background:#e7ebee; height: 100%;/*overflow-x:hidden;*/} 8 | h1,h2,h3,h4,h5,h6,button,input,textarea,select{font-size:100%;} 9 | fieldset,img{border:0;} 10 | table{border-collapse:collapse;border-spacing:0;width:100%;} 11 | caption,th{text-align:left;} 12 | li{list-style:none;} 13 | address,cite,code,dfn,var,em,th,i{font-style:normal;font-weight:normal;} 14 | button,input,textarea{outline:none;font-family:inherit;} 15 | h1,h2,h3,h4,h5,h6 {font-weight: normal;} 16 | a {text-decoration:none;} 17 | a:active,a:hover {outline: 0;text-decoration:none;} 18 | select, dl {word-wrap:normal;} 19 | input,button{border:0px;} 20 | article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary {display: block;} 21 | audio,canvas,progress,video {display: inline-block;vertical-align: baseline;} 22 | i{font-style: normal;} 23 | 24 | .bg-danger {background-color: #f44336;color: #fff !important;} 25 | .list-unstyled{list-style: none;} 26 | .pull-right{float: right !important;} 27 | .has-badge{position: relative;} 28 | .has-badge .badge {position: absolute;right: 0;top: 0;} 29 | .badge { display: inline-block; border-radius: 50%;color: #fff;font-size: 11px;line-height: 16px;padding:0 5px; text-align: center;} 30 | .block {border-bottom: 1px solid #eff2f6;overflow: hidden;padding: 30px 24px;} 31 | .button{cursor:pointer;} 32 | .txt-color-danger{color: #ff4949 !important;} 33 | 34 | .el-button--text:hover{color: #20a0ff} 35 | .el-table .cell{ 36 | padding-left: 12px !important; 37 | padding-right: 12px !important; 38 | } 39 | 40 | .cell .el-button+.el-button{margin-left: 0;} 41 | .el-dropdown-link{cursor: pointer;} 42 | .el-pagination{margin-top:6px;} 43 | /*.el-pagination__total{margin-left: 0 !important;}*/ 44 | 45 | 46 | /*demo begin*/ 47 | .demo h2{margin-bottom: 15px; padding-bottom: 8px; font-weight: bold;} 48 | 49 | /*The Wrapper*/ 50 | .scrollbar-sidebar{ width: 100%; max-height: 100%;} 51 | 52 | /*The Content*/ 53 | .scroll-sidebar{max-width: 260px;} 54 | 55 | .scrollbar-main{width: 100%;max-height: 100%;} 56 | .scroll-main{width: 100%;} 57 | 58 | .vue-scrollbar-transition, .vue-scrollbar__scrollbar-horizontal, .vue-scrollbar__scrollbar-vertical { 59 | transition: all 0.3s ease 0s; 60 | } 61 | .vue-scrollbar-transition--scrollbar { 62 | transition: opacity 0.3s linear 0s; 63 | } 64 | .vue-scrollbar__wrapper { 65 | /*background: #fff none repeat scroll 0 0;*/ 66 | margin: 0 auto; 67 | overflow: hidden; 68 | position: relative; 69 | } 70 | .vue-scrollbar__wrapper:hover .vue-scrollbar__scrollbar-horizontal, .vue-scrollbar__wrapper:hover .vue-scrollbar__scrollbar-vertical { 71 | opacity: 1; 72 | } 73 | .vue-scrollbar__scrollbar-horizontal, .vue-scrollbar__scrollbar-vertical { 74 | background: transparent none repeat scroll 0 0; 75 | opacity: 0.2; 76 | position: absolute; 77 | } 78 | .vue-scrollbar__scrollbar-horizontal:hover, .vue-scrollbar__scrollbar-vertical:hover { 79 | background: rgba(0, 0, 0, 0.3) none repeat scroll 0 0; 80 | } 81 | .vue-scrollbar__scrollbar-horizontal .scrollbar, .vue-scrollbar__scrollbar-vertical .scrollbar { 82 | background: rgba(0, 0, 0, 0.5) none repeat scroll 0 0; 83 | cursor: default; 84 | position: relative; 85 | } 86 | .vue-scrollbar__scrollbar-vertical { 87 | height: 100%; 88 | right: 0; 89 | top: 0; 90 | width: 8px; 91 | } 92 | .vue-scrollbar__scrollbar-vertical .scrollbar { 93 | width: 8px; 94 | } 95 | .vue-scrollbar__scrollbar-horizontal { 96 | bottom: 0; 97 | height: 10px; 98 | right: 0; 99 | width: 100%; 100 | } 101 | .vue-scrollbar__scrollbar-horizontal .scrollbar { 102 | height: 10px; 103 | } 104 | 105 | /* slideout menu begin */ 106 | .sidebar-profile-container{min-height: 60px; vertical-align: middle; padding: 2px 0;} 107 | .sidebar-profile-container i.user{font-size: 40px !important; color: #1665c1;} 108 | .sidebar-profile-container .display-name{line-height: 36px;font-size: 28px;} 109 | .btn-toggle-sidebar {display: inline-block;padding: 0 3px;font-size: 24px;cursor: pointer;background: transparent;transition: all .3s;margin-left: 8px;top:4px;position: absolute;} 110 | 111 | .btn-toggle-sidebar:hover { 112 | transform: scaleX(1.3); 113 | } 114 | 115 | .sidebar-on .btn-toggle-sidebar { 116 | transform: rotate(90deg); 117 | } 118 | 119 | .sidebar-container { 120 | background-color: #fff; 121 | width: 260px; 122 | position: fixed; 123 | height: 100%; 124 | z-index: 1; 125 | top: 0; 126 | left: 0; 127 | /*overflow-x: hidden; 128 | overflow-y: auto;*/ 129 | transition: 0.2s; 130 | color: #6c7177;border-right: 1px solid #ddd;box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.05); 131 | } 132 | 133 | .sidebar-container a { 134 | color: #fff; 135 | } 136 | 137 | .content-container { 138 | margin-left: 260px; 139 | transition: margin-left .2s; 140 | } 141 | 142 | .content-container .content { 143 | width: 100%; 144 | } 145 | 146 | .sidebar-on .sidebar-container { 147 | left: -260px; 148 | border-right:0; 149 | } 150 | .sidebar-on .content-container { 151 | margin-left: 0px; 152 | } 153 | 154 | .sidebar-on .btn-toggle-sidebar:hover { 155 | transform: scaleX(1.3); 156 | } 157 | /*.sidebar-on .sidebar-logo{position: absolute;right: -5px;top: 0; font-size: 32px !important;}*/ 158 | .sidebar-on .display-name{display: none;} 159 | .sidebar-on .btn-toggle-sidebar{position: absolute; left: -3px; z-index: 5; color: #fff;} 160 | .side-menu-item .side-menu-sub{line-height: 24px; display: none;} 161 | .side-menu-sub{-webkit-transition:height 200ms ease-in;-moz-transition:height 200ms ease-in;-o-transition:height 200ms ease-in;transition:height 200ms ease-in;} 162 | .side-menu-item.expand .side-menu-sub{display: block; } 163 | .sidebar-on .side-menu-sub.li{transform: translateY(0%);} 164 | .side-menu-item a{cursor: pointer; display: block;color: inherit;height: 50px;letter-spacing: 0.0125em;line-height: 50px;overflow: hidden;padding: 0 16px;text-decoration: none;} 165 | .side-menu-item .side-menu-folder{background-color: #f8f8f8;border-left: 3px solid #ddd; color: #555;padding: 5px 8px; letter-spacing: 0.05em;display: block; position:relative;} 166 | .side-menu-folder .icon.caret{display: inline-block;position: absolute;right: 3px;top:3px; color: #aaa;transition: transform 0.2s ease 0.1s;width: 24px;height: 48px;} 167 | .side-menu-item a.side-menu-leaf{padding: 4px 8px; padding-left: 20px; transition: background 0.5s;} 168 | .side-menu-item a.side-menu-leaf.active,.side-menu-item a.side-menu-leaf:hover{background-color: #FCFCFC; color: #222} 169 | .side-menu-item .icon.caret:hover,.side-menu-item.expand .icon.caret{transform: rotate(90deg);} 170 | /* slideout menu end */ 171 | 172 | /*toolbar begin*/ 173 | .toolbar-container{background-color: #1976d2;color: #fff;position: relative;} 174 | .toolbar-items{box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.14);line-height: 48px;min-height: 49px;} 175 | .toolbar-menu li{ float: left;} 176 | .toolbar-menu li .toolbar-item{color: #fff;display: block;padding-left: 13px; padding-right: 10px;} 177 | .toolbar-menu li .toolbar-item .el-dropdown{color: #fff;} 178 | .toolbar-menu li .toolbar-item:hover{cursor: pointer; background-color: rgba(0,0,0,0.1)} 179 | .toolbar-menu li .toolbar-item-link{display: inline-block;height: 100%;} 180 | .toolbar-menu-title.has-badge .badge{right: -8px;top:3px;} 181 | /*toolbar end*/ 182 | 183 | /*tabpanel begin*/ 184 | .vue-tabpanel { 185 | position: relative; 186 | } 187 | 188 | .tabs-list-wrapper { 189 | padding: 0; 190 | border-bottom: 1px solid #dadada; 191 | } 192 | 193 | @keyframes loading-rotate { 194 | from { 195 | transform: rotate(0); 196 | } 197 | to { 198 | transform: rotate(360deg); 199 | } 200 | } 201 | 202 | @-webkit-keyframes loading-rotate { 203 | from { 204 | transform: rotate(0); 205 | } 206 | to { 207 | transform: rotate(360deg); 208 | } 209 | } 210 | 211 | .tabs-list { 212 | list-style: none; 213 | margin: 0px; 214 | padding: 0px; 215 | overflow: auto; 216 | width: auto; 217 | zoom: 1; 218 | } 219 | 220 | .tabs-list>li { 221 | display: inline-block; 222 | padding: 8px 24px; 223 | position: relative; 224 | color: #999;user-select: none;-webkit-touch-callout: none; -webkit-user-select: none;-khtml-user-select: none;-moz-user-select: none;-ms-user-select: none; 225 | } 226 | 227 | .tabs-list>li.loading:before { 228 | content: ' '; 229 | box-sizing: border-box; 230 | display: inline-block; 231 | width: 14px; 232 | height: 14px; 233 | position: absolute; 234 | left: 0px; 235 | top: 10px; 236 | border-radius: 9px; 237 | border: 2px solid #1ab394; 238 | border-top-color: transparent; 239 | border-left-color: transparent; 240 | animation: loading-rotate 0.8s infinite linear; 241 | -webkit-animation: loading-rotate 0.8s infinite linear; 242 | } 243 | 244 | .tabs-list>li.active { 245 | color: #333; 246 | } 247 | 248 | .tabs-list>li.active:after { 249 | content: ' '; 250 | position: absolute; 251 | bottom: 0px; 252 | width: 100%; 253 | height: 2px; 254 | background-color: #1ab394; 255 | left: 0px; 256 | } 257 | 258 | .tabs-list>li.active .btn-close { 259 | opacity: 1; 260 | } 261 | 262 | .tabs-list>li:hover { 263 | cursor: pointer; 264 | } 265 | 266 | .tabs-list>li:hover .btn-close { 267 | opacity: 1; 268 | } 269 | 270 | .tabs-list>li .btn-close { 271 | position: absolute; 272 | display: inline-block; 273 | opacity: 0; 274 | top: 2px; 275 | right: -5px; 276 | line-height: 16px; 277 | width: 16px; 278 | height: 16px; 279 | border-radius: 14px; 280 | font-size: 13px; 281 | color: #999; 282 | transition: all 0.2s ease; 283 | } 284 | 285 | .tabs-list>li .btn-close:hover { 286 | color: #333; 287 | transform: scale(1.5); 288 | cursor: pointer; 289 | } 290 | 291 | .tabs-content-wrapper { 292 | position: relative; 293 | overflow-y: auto; 294 | } 295 | 296 | .tabs-content-wrapper .tabs-content { 297 | display: none; 298 | } 299 | 300 | .tabs-content-wrapper .tabs-content.active { 301 | display: block; 302 | background-color: #fff; 303 | border-top: 1px solid #eee; 304 | } 305 | 306 | /*tabpanel begin*/ 307 | 308 | /*toolstrip begin*/ 309 | .toolstrip-container{padding:5px 0; position: relative; clear: both;} 310 | .toolstrip-btn-add-to-workbench{display: inline-block; width: 24px; height: 24px; position: absolute;right: 0; top: 10px;} 311 | .toolstrip-btn-add-to-workbench:hover{cursor: pointer;} 312 | .toolstrip-container .button-item .btn{display: inline-block; margin-right: 8px; padding-left: 5px; padding-right: 5px;} 313 | .toolstrip-container .button-item{border-bottom: 1px solid #eee;margin-bottom: 5px;} 314 | 315 | .ts-search-container{padding: 5px; clear: both;} 316 | 317 | .toolstrip-setting-container{display: inline-block; width: 24px; height: 24px; position: absolute;right: 25px; top: 10px;} 318 | /*toolstrip end*/ 319 | 320 | /*main content begin*/ 321 | .content-container{background-color:f1f2f3 ;color: #6c7177; height: 100%;} 322 | .content{padding: 8px;} 323 | .content-inner{ background-color: #fff;height: 100%;box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.06)} 324 | /*main content end*/ 325 | 326 | @media (max-width:600px) { 327 | 328 | } 329 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/css/vue-tabpanel.css: -------------------------------------------------------------------------------- 1 | .vue-tabpanel { 2 | position: relative; 3 | } 4 | .tabs-list-wrapper { 5 | padding: 2px 8px; 6 | background: #fff; 7 | } 8 | @keyframes loading-rotate { 9 | from { 10 | transform: rotate(0); 11 | } 12 | to { 13 | transform: rotate(360deg); 14 | } 15 | } 16 | @-webkit-keyframes loading-rotate { 17 | from { 18 | transform: rotate(0); 19 | } 20 | to { 21 | transform: rotate(360deg); 22 | } 23 | } 24 | .tabs-list { 25 | list-style: none; 26 | margin: 0px; 27 | padding: 0px; 28 | overflow: auto; 29 | width: auto; 30 | zoom: 1; 31 | } 32 | .tabs-list > li { 33 | float: left; 34 | padding: 6px 18px; 35 | position: relative; 36 | color: #999; 37 | } 38 | .tabs-list > li.loading:before { 39 | content: ' '; 40 | box-sizing: border-box; 41 | display: inline-block; 42 | width: 14px; 43 | height: 14px; 44 | position: absolute; 45 | left: 0px; 46 | top: 10px; 47 | border-radius: 9px; 48 | border: 2px solid #1ab394; 49 | border-top-color: transparent; 50 | border-left-color: transparent; 51 | animation: loading-rotate 0.8s infinite linear; 52 | -webkit-animation: loading-rotate 0.8s infinite linear; 53 | } 54 | .tabs-list > li.active { 55 | color: #333; 56 | } 57 | .tabs-list > li.active:after { 58 | content: ' '; 59 | position: absolute; 60 | bottom: 0px; 61 | width: 100%; 62 | height: 2px; 63 | background-color: #1ab394; 64 | left: 0px; 65 | } 66 | .tabs-list > li.active .btn-close { 67 | opacity: 1; 68 | } 69 | .tabs-list > li:hover { 70 | cursor: pointer; 71 | } 72 | .tabs-list > li:hover .btn-close { 73 | opacity: 1; 74 | } 75 | .tabs-list > li .btn-close { 76 | position: absolute; 77 | display: inline-block; 78 | opacity: 0; 79 | top: 2px; 80 | right: 0px; 81 | line-height: 12px; 82 | width: 14px; 83 | height: 14px; 84 | border-radius: 14px; 85 | font-size: 12px; 86 | color: #999; 87 | transition: all 0.2s ease; 88 | } 89 | .tabs-list > li .btn-close:hover { 90 | color: #333; 91 | transform: scale(1.5); 92 | cursor: pointer; 93 | } 94 | .tabs-content-wrapper { 95 | position: relative; 96 | } 97 | .tabs-content-wrapper .tabs-content { 98 | display: none; 99 | } 100 | .tabs-content-wrapper .tabs-content.active { 101 | display: block; 102 | } 103 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/Axure-Components.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Axure Components_icon 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/Module.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Module_icon 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/Sketch-Template.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sketch Template_icon 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/component.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/component.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/consistency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/consistency.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/controllability.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/controllability.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/efficiency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/efficiency.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/element-demo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/element-demo.jpeg -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/feedback.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/guide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/guide.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/hamburger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/hamburger.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/logo.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/navbar_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/navbar_0.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/navbar_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/navbar_1.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/navbar_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/navbar_2.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/navbar_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/navbar_3.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/qrcode.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/resource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/resource.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/assets/images/stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedefault/tsadmin/e4d067b563afd5f6fc02c351930b5ed1a1f886c6/frontend/tsadmin/app/assets/images/stars.png -------------------------------------------------------------------------------- /frontend/tsadmin/app/components/menu-item.vue: -------------------------------------------------------------------------------- 1 | 14 | 81 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/components/sidebar-menu.vue: -------------------------------------------------------------------------------- 1 | 6 | 26 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/components/toolstrip.vue: -------------------------------------------------------------------------------- 1 | 16 | 31 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/config/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | AUTH_API:'http://localhost:6455/oauth2/token', 3 | SCC_API:'http://localhost:8479/api' 4 | //AUTH_API:'http://192.168.1.88/dxauth/oauth2/token', 5 | //SCC_API:'http://192.168.1.88/dxscc/api' 6 | } 7 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/config/tabs.js: -------------------------------------------------------------------------------- 1 | export default [{ 2 | name: '/homepage', 3 | title: '首页', 4 | component: require('views/homepage.vue') 5 | },{ 6 | name: '/dashboard', 7 | title: '仪表盘', 8 | component: require('views/dashboard.vue') 9 | },{ 10 | name: '/user_management', 11 | title: '仪表盘', 12 | component: require('views/user.vue') 13 | }] 14 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/data/menu.js: -------------------------------------------------------------------------------- 1 | export default [{ 2 | title: '系统设置', 3 | name: '1_system_settings', 4 | expand: true, 5 | uniqueNo: '1_system_settings', 6 | children: [{ 7 | title: '首页', 8 | name: '首页', 9 | path: '/homepage', 10 | allowClose: false, 11 | uniqueNo: 'homepage', 12 | children: [] 13 | }, { 14 | title: '仪表盘', 15 | name: '仪表盘', 16 | path: '/dashboard', 17 | uniqueNo: 'dashboard', 18 | allowClose: true, 19 | children: [] 20 | },{ 21 | title: '用户管理', 22 | name: '用户管理', 23 | path: '/user_management', 24 | allowClose: true, 25 | uniqueNo: '10001_user_management', 26 | children: [] 27 | }, { 28 | title: '角色管理[未实现]', 29 | name: '10002_role_management', 30 | uniqueNo: '10002_role_management', 31 | children: [] 32 | }, { 33 | title: '权限管理[未实现]', 34 | name: '10003_permission_management', 35 | uniqueNo: '10003_permission_management', 36 | children: [] 37 | }] 38 | }] 39 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/pages/dashboard/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 管理系统 14 | 15 | 16 | 17 |
18 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/pages/dashboard/app.js: -------------------------------------------------------------------------------- 1 | import '../../assets/css/main.css'; 2 | import '../../assets/css/semantic-ui-icon.css'; 3 | import '../../assets/css/vue-tabpanel.css'; 4 | import 'element-ui/lib/theme-default/index.css'; 5 | import Vue from 'vue'; 6 | import axios from 'util/http'; 7 | import Element from 'element-ui'; 8 | import { Message, MessageBox } from 'element-ui'; 9 | import entry from './app.vue'; 10 | import Vuex from 'vuex'; 11 | import VueTabpanel from 'vue-tabpanel'; 12 | import tabs from 'config/tabs.js'; 13 | import config from 'config/config.js'; 14 | import store from '_vuex/store'; 15 | import toolstrip from 'components/toolstrip'; 16 | import VueResource from 'vue-resource'; 17 | //import Slideout from 'slideout'; 18 | 19 | const Tabpanel = new VueTabpanel({ 20 | tabs, 21 | persist: false 22 | }); 23 | 24 | Tabpanel.beforeCloseEach((tab, next) => { 25 | console.info(app); 26 | MessageBox.confirm('确定要关闭窗口['+tab.title+']吗?', '窗口关闭提示', { 27 | confirmButtonText: '确 定', 28 | cancelButtonText: '取 消', 29 | type: 'warning' 30 | }).then(() => { 31 | Message({ 32 | type: 'success', 33 | message: '成功!' 34 | }); 35 | next() 36 | }).catch(()=>{ 37 | 38 | }); 39 | }) 40 | 41 | Vue.use(VueTabpanel); 42 | Vue.use(Element); 43 | Vue.use(Vuex); 44 | Vue.use(VueResource); 45 | 46 | Vue.component('toolstrip', toolstrip); 47 | 48 | Vue.prototype.$axios = axios; 49 | Vue.prototype.api_url = 'http://localhost:12548/api'; 50 | 51 | //Vue.component('slideout', Slideout); 52 | 53 | //Vue.http.headers.common['Authorization'] = 'bearer ' + _token; 54 | 55 | let app = new Vue({ 56 | render: h => h(entry), 57 | taber: Tabpanel, 58 | axios, 59 | store, 60 | mounted() { }, 61 | created() { } 62 | }); 63 | app.$mount('#app'); 64 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/pages/dashboard/app.vue: -------------------------------------------------------------------------------- 1 | 80 | 81 | 207 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/pages/login/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 登录 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/pages/login/app.js: -------------------------------------------------------------------------------- 1 | import '../../assets/css/main.css'; 2 | import '../../assets/css/semantic-ui-icon.css'; 3 | import 'element_ui/lib/theme-default/index.css'; 4 | import Vue from 'vue'; 5 | import ElementUI from 'element-ui'; 6 | import App from './app.vue'; 7 | import config from 'config/config.js'; 8 | import Vuex from 'vuex'; 9 | import store from '_vuex/store'; 10 | 11 | Vue.use(ElementUI); 12 | Vue.use(Vuex); 13 | let vm = new Vue({ 14 | el: '#app', 15 | store, 16 | render: h => h(App) 17 | }) 18 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/pages/login/app.vue: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/util/http.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { 3 | Loading, 4 | MessageBox, 5 | Notification 6 | } from 'element-ui'; 7 | //import store from './store/store' 8 | //import * as types from './store/types' 9 | 10 | // axios 配置 11 | axios.defaults.timeout = 15000; 12 | //axios.defaults.headers.common['Authorization'] = 'Authorization'; 13 | //axios.defaults.baseURL = 'http://localhost:12548/api'; 14 | axios.defaults.baseURL ='http://localhost:12548/api'; 15 | axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; 16 | if (IS_PRODUCTION) { 17 | axios.defaults.baseURL = "http://localhost:12548/api"; 18 | } 19 | 20 | 21 | let loadingInstance; 22 | // http request 拦截器 23 | axios.interceptors.request.use( 24 | config => { 25 | /* 26 | if (store.state.token) { 27 | config.headers.Authorization = `token ${store.state.token}`; 28 | } 29 | */ 30 | loadingInstance = Loading.service({ 31 | text: '加载中...' 32 | }); 33 | return config; 34 | }, 35 | function (error) { 36 | console.info(error); 37 | return Promise.reject(error); 38 | }); 39 | 40 | // http response 拦截器 41 | axios.interceptors.response.use( 42 | response => { 43 | loadingInstance.close(); 44 | return response; 45 | }, 46 | error => { 47 | try { 48 | loadingInstance.close(); 49 | if (error.response) { 50 | let res = error.response; 51 | let message = '处理请求时遇到了问题,请稍候再试.'; 52 | if (res.data && res.data.message) { 53 | message = res.data.message; 54 | } 55 | alert(res.status); 56 | switch (res.status) { 57 | case 401: 58 | // 401 清除token信息并跳转到登录页面 59 | //store.commit(types.LOGOUT); 60 | /* 61 | router.replace({ 62 | path: 'login', 63 | query: {redirect: router.currentRoute.fullPath} 64 | }) 65 | */ 66 | Notification({ 67 | title: '提示', 68 | message: '登录已失效,请重新登录', 69 | type: 'error' 70 | }); 71 | window.top.location.href = "/login.html"; 72 | break; 73 | default: 74 | Notification({ 75 | title: '错误提示', 76 | message: message, 77 | type: 'error', 78 | duration: 0 79 | }); 80 | break; 81 | } 82 | } 83 | let err = error + ''; 84 | if (err.indexOf("Network Error") > -1) { 85 | MessageBox({ 86 | title: '网络出错', 87 | message: '向服务器发起资源请求时出错,请检查你的网络或者联系管理员检查服务器运行状况.', 88 | type: 'error', 89 | closeOnClickModal: false, 90 | closeOnPressEscape: false 91 | }); 92 | } 93 | return Promise.reject(error.response.data); 94 | } catch (err) { } 95 | }); 96 | 97 | export default axios; 98 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/util/util.js: -------------------------------------------------------------------------------- 1 | var SIGN_REGEXP = /([yMdhsm])(\1*)/g; 2 | var DEFAULT_PATTERN = 'yyyy-MM-dd'; 3 | 4 | function padding(s, len) { 5 | var len = len - (s + '').length; 6 | for (var i = 0; i < len; i++) { 7 | s = '0' + s; 8 | } 9 | return s; 10 | }; 11 | 12 | export default { 13 | searchPredicate: { 14 | int: { 15 | value: 'int' 16 | }, 17 | string: { 18 | value: 'string' 19 | }, 20 | date: { 21 | value: 'date' 22 | }, 23 | boolean: { 24 | value: 'boolean' 25 | }, 26 | select: { 27 | value: 'select' 28 | } 29 | }, 30 | getQueryStringByName: function(name) { 31 | var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); 32 | var r = window.location.search.substr(1).match(reg); 33 | var context = ""; 34 | if (r != null) 35 | context = r[2]; 36 | reg = null; 37 | r = null; 38 | return context == null || context == "" || context == "undefined" ? "" : context; 39 | }, 40 | formatDate: { 41 | format: function(date, pattern) { 42 | pattern = pattern || DEFAULT_PATTERN; 43 | return pattern.replace(SIGN_REGEXP, function($0) { 44 | switch ($0.charAt(0)) { 45 | case 'y': 46 | return padding(date.getFullYear(), $0.length); 47 | case 'M': 48 | return padding(date.getMonth() + 1, $0.length); 49 | case 'd': 50 | return padding(date.getDate(), $0.length); 51 | case 'w': 52 | return date.getDay() + 1; 53 | case 'h': 54 | return padding(date.getHours(), $0.length); 55 | case 'm': 56 | return padding(date.getMinutes(), $0.length); 57 | case 's': 58 | return padding(date.getSeconds(), $0.length); 59 | } 60 | }); 61 | }, 62 | parse: function(dateString, pattern) { 63 | var matchs1 = pattern.match(SIGN_REGEXP); 64 | var matchs2 = dateString.match(/(\d)+/g); 65 | if (matchs1.length == matchs2.length) { 66 | var _date = new Date(1970, 0, 1); 67 | for (var i = 0; i < matchs1.length; i++) { 68 | var _int = parseInt(matchs2[i]); 69 | var sign = matchs1[i]; 70 | switch (sign.charAt(0)) { 71 | case 'y': 72 | _date.setFullYear(_int); 73 | break; 74 | case 'M': 75 | _date.setMonth(_int - 1); 76 | break; 77 | case 'd': 78 | _date.setDate(_int); 79 | break; 80 | case 'h': 81 | _date.setHours(_int); 82 | break; 83 | case 'm': 84 | _date.setMinutes(_int); 85 | break; 86 | case 's': 87 | _date.setSeconds(_int); 88 | break; 89 | } 90 | } 91 | return _date; 92 | } 93 | return null; 94 | } 95 | }, 96 | validate: { 97 | isNumber(n) { 98 | return !isNaN(parseFloat(n)) && isFinite(n); 99 | }, 100 | validateAsync(rule, value, callback) { 101 | let pkid = ''; 102 | if (this.pageInfo.pkid !== null) { 103 | pkid = this.pageInfo.pkid; 104 | } 105 | this.$http.get(this.$CONFIG('SCC_API') + rule.url + "?guid=" + this.pageInfo.guid + "&pkid=" + pkid + "&col_name=" + rule.fullField + "&val=" + value).then(function(response) { 106 | let data = response.body; 107 | if (data.success) { 108 | if (data.pass) { 109 | callback(); 110 | } else { 111 | callback(new Error(data.message || rule.message || "验证失败,请检查!")) 112 | } 113 | } else { 114 | callback(new Error(data.error || rule.message || "验证失败,请检查!")) 115 | } 116 | 117 | }, function(error) { 118 | callback(new Error(error)) 119 | }); 120 | }, 121 | }, 122 | deepClone: function(target, source) { 123 | /* deep clone source object to target object, 124 | giving the last one precedence */ 125 | 126 | if (typeof target !== 'object') { 127 | target = {}; 128 | } 129 | if (Array.isArray(source)) { 130 | return source.slice(); 131 | } 132 | for (const property in source) { 133 | if (source.hasOwnProperty(property)) { 134 | const sourceProperty = source[property]; 135 | if (typeof sourceProperty === 'object') { 136 | target[property] = this.deepClone(target[property], sourceProperty); 137 | continue; 138 | } 139 | target[property] = sourceProperty; 140 | } 141 | } 142 | return target; 143 | } 144 | }; 145 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/views/dashboard.vue: -------------------------------------------------------------------------------- 1 | 107 | 108 | 408 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/views/homepage.vue: -------------------------------------------------------------------------------- 1 | 45 | 77 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/views/user.vue: -------------------------------------------------------------------------------- 1 | 107 | 108 | 408 | -------------------------------------------------------------------------------- /frontend/tsadmin/app/vuex/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | Vue.use(Vuex); 4 | const store = new Vuex.Store({ 5 | state: { 6 | sidebar: { 7 | collapsed: false 8 | }, 9 | user: {}, 10 | permissions: [], 11 | loggedIn: false, 12 | theme: 'default', 13 | workbench: [] 14 | }, 15 | mutations: { 16 | TOGGLE_SIDEBAR(state) { 17 | state.sidebar.collapsed = !state.sidebar.collapsed 18 | }, 19 | LOGGED_IN(state) { 20 | state.loggedIn = true; 21 | }, 22 | LOGOUT(state) { 23 | state.loggedIn = false; 24 | state.user = {}; 25 | state.permissions = []; 26 | localStorage.setItem('access_token', ''); 27 | }, 28 | LOGGED_IN_USER(state, data) { 29 | state.user = data.user; 30 | state.permissions = data.permissions; 31 | }, 32 | SWITCH_THEME(state, theme) { 33 | localStorage.setItem('dx_ls_theme', theme); 34 | state.theme = theme; 35 | }, 36 | WORKBENCH_ADD(state,tab){ 37 | if(state.workbench.indexOf(tab)>-1){ 38 | return; 39 | } 40 | state.workbench.push(tab); 41 | }, 42 | WORKBENCH_REMOVE(state,tab){ 43 | let index=state.workbench.indexOf(tab); 44 | if(index===-1){ 45 | return; 46 | } 47 | state.workbench.splice(index,1); 48 | }, 49 | WORKBENCH_CLEAR(state){ 50 | state.workbench=[]; 51 | } 52 | }, 53 | getters: { 54 | currentTheme: state => { 55 | let currentTheme = localStorage.getItem('dx_ls_theme'); 56 | if (currentTheme == null) { 57 | currentTheme = "default"; 58 | } 59 | return currentTheme; 60 | }, 61 | workbench:state=>{ 62 | return state.workbench; 63 | } 64 | } 65 | }) 66 | export default store; 67 | -------------------------------------------------------------------------------- /frontend/tsadmin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TsAdmin", 3 | "description": "TsAdmin admin dashboard project.", 4 | "author": "TsAdmin Team", 5 | "private": true, 6 | "scripts": { 7 | "dev": "cross-env NODE_ENV=development webpack-dev-server --inline --hot --port 8860", 8 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.16.1", 12 | "babel-core": "^6.22.1", 13 | "babel-loader": "^6.2.10", 14 | "babel-plugin-transform-runtime": "^6.22.0", 15 | "babel-preset-es2015": "^6.22.0", 16 | "babel-preset-stage-2": "^6.24.1", 17 | "babel-runtime": "^6.23.0", 18 | "cross-env": "^4.0.0", 19 | "css-loader": "^0.28.0", 20 | "element-ui": "^1.3.1", 21 | "extract-text-webpack-plugin": "^2.1.0", 22 | "file-loader": "^0.11.1", 23 | "glob": "^7.1.1", 24 | "html-loader": "^0.4.5", 25 | "html-webpack-plugin": "^2.28.0", 26 | "style-loader": "^0.16.1", 27 | "url-loader": "^0.5.8", 28 | "vue": "^2.5.16", 29 | "vue-loader": "^12.0.2", 30 | "vue-resource": "^1.3.1", 31 | "vue-tabpanel": "1.3.0", 32 | "vue-template-compiler": "2.5.16", 33 | "vue2-scrollbar": "^0.0.1", 34 | "vuex": "^2.3.1", 35 | "webpack": "^2.4.1", 36 | "webpack-dev-server": "^2.4.5" 37 | }, 38 | "devDependencies": {} 39 | } 40 | -------------------------------------------------------------------------------- /frontend/tsadmin/server.js: -------------------------------------------------------------------------------- 1 | //var TARGET = 'https://github.com'; 2 | var express = require('express'); 3 | var app = express(); 4 | var http = require('http'); 5 | var httpProxy = require('http-proxy'); 6 | var proxy = httpProxy.createProxyServer({}); 7 | 8 | app.use(express.static('./dist')); 9 | 10 | // app.all('/*', function(req, res, next) { 11 | // console.log(req.url); 12 | // return proxy.web(req, res, { 13 | // target: TARGET 14 | // }); 15 | // // next(); 16 | // }); 17 | 18 | app.get('/', function(req, res) { 19 | res.send('Hello Vue'); 20 | }); 21 | 22 | app.listen(2333); 23 | -------------------------------------------------------------------------------- /frontend/tsadmin/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | var glob = require('glob'); 4 | 5 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 6 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 7 | var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin'); 8 | 9 | let _publicPath = '/'; 10 | if (process.env.NODE_ENV === 'production') { 11 | _publicPath = "./"; 12 | } 13 | var entries = {}; 14 | var chunks = []; 15 | getEntriesAndChunks(); 16 | 17 | console.info("entries:", entries); 18 | console.info("chunks:", chunks); 19 | 20 | var config = { 21 | entry: entries, 22 | output: { 23 | path: path.resolve(__dirname, './dist'), 24 | filename: '[name].js', 25 | publicPath: _publicPath 26 | }, 27 | resolve: { 28 | //配置别名,在项目中可缩减引用路径 29 | extensions: ['.js', '.vue', '.css'], 30 | alias: { 31 | assets: path.join(__dirname, '/app/assets'), 32 | components: path.join(__dirname, '/app/components'), 33 | data: path.join(__dirname, '/app/data'), 34 | views: path.join(__dirname, '/app/views'), 35 | config: path.join(__dirname, '/app/config'), 36 | util: path.join(__dirname, '/app/util'), 37 | _vuex: path.join(__dirname, '/app/vuex'), 38 | element_ui: path.join(__dirname, 'node_modules/element-ui'), 39 | scrollbar: path.join(__dirname, 'node_modules/vue2-scrollbar'), 40 | root: path.join(__dirname, 'node_modules') 41 | } 42 | }, 43 | module: { 44 | loaders: [{ 45 | test: /\.vue$/, 46 | loader: 'vue-loader' 47 | }, 48 | { 49 | test: /\.js$/, 50 | loader: 'babel-loader?presets=es2015', 51 | exclude: /node_modules/ 52 | }, 53 | { 54 | test: /\.css$/, 55 | loader: ExtractTextPlugin.extract({ 56 | fallbackLoader: 'style-loader', 57 | loader: 'css-loader' 58 | }) 59 | }, 60 | { 61 | test: /\.(jpe|jpg|woff|woff2|eot|ttf|svg)(\?.*$|$)/, 62 | loader: 'url-loader?importLoaders=1&limit=100000', 63 | query: { 64 | name: '[name].[ext]?[hash]' 65 | } 66 | }, 67 | { 68 | test: /\.(png|jpg|gif|svg)$/, 69 | loader: 'file-loader', 70 | query: { 71 | name: '[name].[ext]?[hash]' 72 | } 73 | } 74 | ] 75 | }, 76 | plugins: [ 77 | new CommonsChunkPlugin({ 78 | name: 'vendors', 79 | filename: 'assets/js/vendors.js', 80 | chunks: chunks, 81 | minChunks: chunks.length 82 | }), 83 | new ExtractTextPlugin({ 84 | filename: 'assets/css/main.css', 85 | allChunks: true 86 | }) 87 | ], 88 | devServer: { 89 | historyApiFallback: false, 90 | noInfo: true, 91 | // proxy: { 92 | // '/github': { 93 | // target: 'https://github.com/github', 94 | // changeOrigin: true, 95 | // pathRewrite: {'^/github' : ''} 96 | // } 97 | // }, 98 | }, 99 | devtool: '#eval-source-map' 100 | }; 101 | 102 | var pages = getHtmls(); 103 | pages.forEach(function(pathname) { 104 | // filename 用文件夹名字 105 | var conf = { 106 | filename: pathname.substring(6, pathname.length - 4) + '.html', //生成的html存放路径,相对于path 107 | template: 'app/' + pathname + '.html', //html模板路径 108 | }; 109 | 110 | var chunk = pathname.substring(6, pathname.length); 111 | if (chunks.indexOf(chunk) > -1) { 112 | conf.inject = 'body'; 113 | conf.chunks = ['vendors', chunk]; 114 | } 115 | if (process.env.NODE_ENV === 'production') { 116 | conf.hash = true; 117 | } 118 | console.info(conf); 119 | config.plugins.push(new HtmlWebpackPlugin(conf)); 120 | //config.plugins.filenameTemplate='./'+config.plugins.filenameTemplate; 121 | //config.plugins.filename='./'+config.plugins.filename; 122 | console.info("config.plugins:", config.plugins); 123 | }); 124 | 125 | module.exports = config; 126 | 127 | function getEntriesAndChunks() { 128 | glob.sync('./app/pages/**/*.js').forEach(function(name) { 129 | var n = name.slice(name.lastIndexOf('app/') + 10, name.length - 3); 130 | entries[n] = [name]; 131 | console.log('getEntriesAndChunks:', n); 132 | chunks.push(n); 133 | }); 134 | } 135 | 136 | function getHtmls() { 137 | var htmls = []; 138 | glob.sync('./app/pages/**/*.html').forEach(function(name) { 139 | var n = name.slice(name.lastIndexOf('app/') + 4, name.length - 5); 140 | console.log('getHtmls:', n); 141 | htmls.push(n); 142 | }); 143 | return htmls; 144 | } 145 | 146 | 147 | if (process.env.NODE_ENV === 'production') { 148 | module.exports.devtool = '#source-map'; 149 | // http://vue-loader.vuejs.org/en/workflow/production.html 150 | module.exports.plugins = (module.exports.plugins || []).concat([ 151 | new webpack.DefinePlugin({ 152 | 'process.env': { 153 | NODE_ENV: '"production"' 154 | }, 155 | IS_PRODUCTION: JSON.stringify(true) 156 | }), 157 | new webpack.optimize.UglifyJsPlugin({ 158 | compress: { 159 | warnings: false 160 | } 161 | }) 162 | ]); 163 | } else { 164 | module.exports.plugins = (module.exports.plugins || []).concat([ 165 | new webpack.DefinePlugin({ 166 | IS_PRODUCTION: JSON.stringify(false) 167 | }) 168 | ]); 169 | } 170 | --------------------------------------------------------------------------------