├── src ├── images │ ├── doc.png │ ├── file.png │ ├── wall.jpg │ ├── chrome.png │ ├── safari.png │ └── firefox.png ├── global.less ├── main.js ├── service │ ├── event.js │ ├── global-var.js │ ├── util.js │ ├── keyboard.js │ ├── file.js │ ├── app-controller.js │ ├── app.js │ └── storage.js ├── components │ ├── apps │ │ ├── custom-app.vue │ │ ├── props.vue │ │ ├── browser.vue │ │ └── calculator.vue │ ├── context-menu.vue │ ├── file-item.vue │ └── app-window.vue ├── views │ ├── application.vue │ ├── root.vue │ ├── wallpaper.vue │ ├── mouse-menu.vue │ ├── selection.vue │ ├── taskbar.vue │ └── file.vue ├── main.less └── data │ ├── files.js │ └── menu.js ├── webpack.config.js ├── dev.js ├── package.json └── README.md /src/images/doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chairuosen/desktop-simulation/HEAD/src/images/doc.png -------------------------------------------------------------------------------- /src/images/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chairuosen/desktop-simulation/HEAD/src/images/file.png -------------------------------------------------------------------------------- /src/images/wall.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chairuosen/desktop-simulation/HEAD/src/images/wall.jpg -------------------------------------------------------------------------------- /src/images/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chairuosen/desktop-simulation/HEAD/src/images/chrome.png -------------------------------------------------------------------------------- /src/images/safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chairuosen/desktop-simulation/HEAD/src/images/safari.png -------------------------------------------------------------------------------- /src/images/firefox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chairuosen/desktop-simulation/HEAD/src/images/firefox.png -------------------------------------------------------------------------------- /src/global.less: -------------------------------------------------------------------------------- 1 | @blue:#4b8de4; 2 | .Filter(@val){ 3 | -webkit-filter: @val; 4 | -moz-filter: @val; 5 | -o-filter: @val; 6 | -ms-filter: @val; 7 | } -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | var Vue = require('vue'); 2 | var jQuery = require('jquery'); 3 | window.$ = jQuery; 4 | window.jQuery = jQuery; 5 | window.Vue = Vue; 6 | window.$event = require('service/event'); 7 | new Vue(require('views/root.vue')); 8 | -------------------------------------------------------------------------------- /src/service/event.js: -------------------------------------------------------------------------------- 1 | var $ = require('jquery'); 2 | module.exports = { 3 | on:function (name,cb) { 4 | $(document).on(name,function (e,data){ 5 | cb(data); 6 | }); 7 | }, 8 | emit:function (name,data) { 9 | $(document).trigger(name,data); 10 | } 11 | } -------------------------------------------------------------------------------- /src/service/global-var.js: -------------------------------------------------------------------------------- 1 | var $w = $(window); 2 | $w.on('resize',update); 3 | 4 | window._h = $w.height()-40; 5 | window._w = $w.width(); 6 | 7 | function update(){ 8 | window._h = $w.height()-40; // taskbar 9 | window._w = $w.width(); 10 | $event && $event.emit('window:resize'); 11 | } -------------------------------------------------------------------------------- /src/service/util.js: -------------------------------------------------------------------------------- 1 | function arrRemove(arr) { 2 | var what, a = arguments, L = a.length, ax; 3 | while (L > 1 && arr.length) { 4 | what = a[--L]; 5 | while ((ax= arr.indexOf(what)) !== -1) { 6 | arr.splice(ax, 1); 7 | } 8 | } 9 | return arr; 10 | } 11 | 12 | module.exports = { 13 | clone:function clone(a) { 14 | return JSON.parse(JSON.stringify(a)); 15 | }, 16 | arrayRemove:function (arr,cb) { 17 | var targetArr = arr.filter(cb); 18 | arrRemove.apply(null,[arr].concat(targetArr)); 19 | } 20 | } -------------------------------------------------------------------------------- /src/components/apps/custom-app.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | Hello World! 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/views/application.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main.less: -------------------------------------------------------------------------------- 1 | *{ 2 | margin:0; 3 | padding:0; 4 | border:none; 5 | box-sizing:border-box; 6 | -webkit-user-select:none; 7 | } 8 | html,body{ 9 | height:100%; 10 | width:100%; 11 | overflow:hidden; 12 | } 13 | .root{ 14 | height:100%; 15 | width:100%; 16 | position:relative; 17 | .scope{ 18 | position:absolute; 19 | top:0; 20 | left:0; 21 | width:100%; 22 | } 23 | } 24 | 25 | .icon{ 26 | background-image: url(./images/doc.png); 27 | background-position: center center; 28 | background-repeat:no-repeat; 29 | &.firefox{ 30 | background-image:url(./images/firefox.png); 31 | } 32 | &.chrome{ 33 | background-image:url(./images/chrome.png); 34 | } 35 | &.safari{ 36 | background-image:url(./images/safari.png); 37 | } 38 | } 39 | 40 | input[type=text]{ 41 | border:1px solid; 42 | cursor:text; 43 | -webkit-user-select:initial; 44 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | var path = require('path'); 4 | 5 | 6 | var htmlOption = { 7 | title:"Desktop Simulation" 8 | } 9 | 10 | module.exports = { 11 | resolve:{ 12 | root:[path.join(__dirname,"src")] 13 | }, 14 | entry:{ 15 | 'main':['./src/main.js'] 16 | }, 17 | output:{ 18 | path:'./dist', 19 | publicPath:'./', 20 | filename:'[name].[hash].js' 21 | }, 22 | module: { 23 | loaders: [ 24 | { 25 | test: /\.less$/, 26 | loader: "style!css!less" 27 | }, 28 | { 29 | test: /\.html$/, 30 | loader: "html" 31 | }, 32 | { 33 | test: /\.vue$/, 34 | loader: "vue" 35 | }, 36 | { 37 | test: /\.(png|jpg)$/, 38 | loader: 'url-loader?limit=8192' 39 | } 40 | ] 41 | }, 42 | devtool:"cheap-module-source-map", 43 | plugins:[ 44 | new HtmlWebpackPlugin(htmlOption) 45 | ] 46 | } -------------------------------------------------------------------------------- /dev.js: -------------------------------------------------------------------------------- 1 | var WebpackDevServer = require("webpack-dev-server"); 2 | var webpack = require("webpack"); 3 | var config = require('./webpack.config.js'); 4 | var port = process.argv[2] || 3000; 5 | 6 | [ 7 | "webpack-dev-server/client?http://localhost:"+port, 8 | "webpack/hot/dev-server", 9 | ].forEach(function(one){ 10 | config.entry.main.unshift(one); 11 | }) 12 | config.plugins.unshift(new webpack.HotModuleReplacementPlugin()); 13 | config.plugins.push(function() { 14 | this.plugin('done', function(stats) { 15 | setTimeout(function () { 16 | console.log('====================================') 17 | console.log('Server listen: http://localhost:'+port); 18 | console.log('====================================') 19 | },0); 20 | }) 21 | }) 22 | config.output.path = "/"; 23 | config.debug = true; 24 | var server = new WebpackDevServer(webpack(config), { 25 | contentBase: "./dist/", 26 | 27 | hot: true, 28 | 29 | historyApiFallback: true, 30 | 31 | filename: config.entry.main[0], 32 | publicPath: "/", 33 | stats: { 34 | colors: true 35 | } 36 | }); 37 | server.listen(port, "localhost"); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "desktop-simulation", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "webpack", 9 | "dev": "node dev.js", 10 | "clean": "rm -rf ./dist" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/chairuosen/desktop-simulation.git" 15 | }, 16 | "author": "chairuosen", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/chairuosen/desktop-simulate/issues" 20 | }, 21 | "homepage": "https://github.com/chairuosen/desktop-simulate#readme", 22 | "devDependencies": { 23 | "babel-core": "^6.9.1", 24 | "babel-loader": "^6.2.4", 25 | "babel-plugin-transform-runtime": "^6.9.0", 26 | "babel-preset-es2015": "^6.9.0", 27 | "babel-runtime": "^6.9.2", 28 | "css-loader": "^0.23.1", 29 | "file-loader": "^0.8.5", 30 | "html-loader": "^0.4.3", 31 | "html-webpack-plugin": "^2.21.0", 32 | "jquery": "^3.0.0", 33 | "less": "^2.7.1", 34 | "less-loader": "^2.2.3", 35 | "style-loader": "^0.13.1", 36 | "url-loader": "^0.5.7", 37 | "vue": "^1.0.24", 38 | "vue-hot-reload-api": "^1.2.0", 39 | "vue-html-loader": "^1.2.2", 40 | "vue-loader": "^3.0.4", 41 | "vue-style-loader": "^1.0.0", 42 | "webpack": "^1.13.1", 43 | "webpack-dev-server": "^1.14.1" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/views/root.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/data/files.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | name: "Safari", 4 | app: "browser", 5 | icon: "safari", 6 | options: { 7 | data: { 8 | index: "http://baidu.com" 9 | }, 10 | height: function () { 11 | return window._h * 0.8; 12 | }, 13 | width: function () { 14 | return window._w * 0.8; 15 | } 16 | } 17 | }, 18 | { 19 | name: "Chrome", 20 | app: "browser", 21 | icon: "chrome", 22 | options: function () { 23 | return { 24 | data: { 25 | index: "http://v2ex.com" 26 | }, 27 | height: window._h * 0.8, 28 | width: window._w * 0.8 29 | } 30 | } 31 | }, 32 | { 33 | name: "Firefox", 34 | app: "browser", 35 | icon: "firefox", 36 | options: { 37 | data: { 38 | index: "http://qq.com" 39 | }, 40 | singleton: true 41 | // resizable:false 42 | } 43 | }, 44 | { 45 | name: "计算器", 46 | app: "calculator", 47 | icon: "", 48 | options: { 49 | // resizable:false, 50 | width:400, 51 | height:400, 52 | } 53 | }, 54 | { 55 | name: "自定义App", 56 | app: "custom-app", 57 | icon: "", 58 | options: {} 59 | } 60 | ]; 61 | -------------------------------------------------------------------------------- /src/service/keyboard.js: -------------------------------------------------------------------------------- 1 | 2 | function change(){ 3 | $.each(combination,function (k,v) { 4 | v.forEach(function (arr) { 5 | var flag = true; 6 | arr.forEach(function (code) { 7 | if(!keypressMap[code]){ 8 | flag = false; 9 | } 10 | }); 11 | if(flag){ 12 | $event.emit(k+':keyboard'); 13 | } 14 | }) 15 | }) 16 | } 17 | 18 | function isA2Z(e){ 19 | if (e.keyCode >= 65 && e.keyCode <= 90) { 20 | return true; 21 | } 22 | return false; 23 | } 24 | var k = { 25 | 'cmd':91, 26 | 'ctrl':17, 27 | 'c':67, 28 | 'v':86, 29 | 'x':88, 30 | 'a':65 31 | } 32 | var combination = { 33 | "copy":[ [k.cmd,k.c], [k.ctrl,k.c] ], 34 | "paste":[ [k.cmd,k.v], [k.ctrl,k.v] ], 35 | "cut":[ [k.cmd,k.x], [k.ctrl,k.x] ], 36 | "selectAll":[ [k.cmd,k.a], [k.ctrl,k.a] ] 37 | } 38 | 39 | var keypressMap = []; 40 | module.exports = { 41 | keypressMap:keypressMap, 42 | init:function () { 43 | $(window).off('keydown keyup').on('keydown',function (e) { 44 | keypressMap[e.keyCode] = true; 45 | change(); 46 | if(isA2Z(e)){ 47 | setTimeout(function () { 48 | keypressMap[e.keyCode] = false; 49 | change(); 50 | },100) 51 | } 52 | }).on('keyup',function (e) { 53 | keypressMap[e.keyCode] = false; 54 | change(); 55 | }); 56 | }, 57 | keyMap:k 58 | }; -------------------------------------------------------------------------------- /src/components/apps/props.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 25 | 26 | {{openedFile.name}} 27 | 目标: 28 | 29 | {{ openedFile.options.data | json }} 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/components/context-menu.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 30 | 31 | 36 | {{item.text}} 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/service/file.js: -------------------------------------------------------------------------------- 1 | function File(o){ 2 | $.extend(this,o); 3 | if(!this.x){ 4 | this.x = 0; 5 | } 6 | if(!this.y){ 7 | this.y = 0; 8 | } 9 | if(!this.inPosition){ 10 | this.inPosition = false; 11 | } 12 | this._sourceData = o; 13 | this.selected = false; 14 | } 15 | File.prototype.open = function () { 16 | var file = this; 17 | var options = { 18 | title:file.name, 19 | type:file.app, 20 | icon:file.icon 21 | }; 22 | $.extend(options,file.getOptions()); 23 | 24 | if(file._openedApp && file._openedApp._close){ 25 | file._openedApp = null; 26 | } 27 | var App = require('service/app.js'); 28 | var app = new App(options); 29 | 30 | if(app.singleton && file._openedApp){ 31 | app = file._openedApp; 32 | } 33 | 34 | require('service/app-controller').openApp(app); 35 | 36 | file.selected = false; 37 | if(app.singleton){ 38 | file._openedApp = app; 39 | } 40 | }; 41 | File.prototype.getOptions = function () { 42 | var options = {}; 43 | if(this.options){ 44 | options = this.options; 45 | if(typeof this.options == 'function'){ 46 | options = this.options(); 47 | } 48 | options = $.extend({},options); 49 | 50 | for (var k in options){ 51 | if(typeof options[k] == "function" ){ 52 | options[k] = options[k](); 53 | } 54 | } 55 | } 56 | return options; 57 | }; 58 | File.prototype.select = function () { 59 | this.selected = !this.selected; 60 | }; 61 | 62 | File.prototype.clone = function () { 63 | var _this = this; 64 | var newObject = {}; 65 | $.each(_this,function (k,v) { 66 | if(_this.hasOwnProperty(k)){ 67 | newObject[k] = v; 68 | } 69 | }); 70 | return new File(newObject); 71 | }; 72 | 73 | module.exports = File; -------------------------------------------------------------------------------- /src/data/menu.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | file:[ 3 | { 4 | text:"打开", 5 | type:"open", 6 | disabled:false, 7 | callback:function () { 8 | $event.emit('open:menu') 9 | } 10 | }, 11 | { 12 | text:"复制", 13 | type:"copy", 14 | disabled:false, 15 | callback:function () { 16 | $event.emit('copy:menu') 17 | } 18 | }, 19 | { 20 | type:"divide" 21 | }, 22 | { 23 | text:"删除", 24 | type:"delete", 25 | disabled:false, 26 | callback:function () { 27 | $event.emit('delete:menu') 28 | } 29 | }, 30 | { 31 | text:"属性", 32 | type:"prop", 33 | callback:function (file) { 34 | $event.emit('openFileProp:menu',file); 35 | } 36 | } 37 | ], 38 | wallpaper:[ 39 | { 40 | text:"刷新", 41 | type:"refresh", 42 | disabled:false, 43 | callback:function () { 44 | // console.log(1); 45 | $event.emit('refresh:menu') 46 | } 47 | }, 48 | { 49 | text:'全选', 50 | type:"selectAll", 51 | callback:function () { 52 | $event.emit('selectAll:menu'); 53 | } 54 | }, 55 | { 56 | text:"粘贴", 57 | type:"paste", 58 | disabled:true, 59 | callback:function () { 60 | $event.emit('paste:menu') 61 | } 62 | }, 63 | { 64 | type:'divide' 65 | }, 66 | { 67 | text:"恢复图标", 68 | type:"resetAllFile", 69 | callback:function () { 70 | $event.emit('resetFile:menu'); 71 | } 72 | } 73 | ] 74 | } -------------------------------------------------------------------------------- /src/service/app-controller.js: -------------------------------------------------------------------------------- 1 | var util = require('service/util'); 2 | var App = require('service/app'); 3 | var File = require('service/file'); 4 | var Vue = require('vue'); 5 | // var storage = require('service/storage'); 6 | 7 | var sourceFile = require('data/files.js'); 8 | 9 | var _this = { 10 | apps:[], 11 | files:(sourceFile).map(function(a){ 12 | return new File(a); 13 | }), 14 | openApp:function (app) { 15 | if(this.apps.indexOf(app)==-1){ 16 | this.apps.push(app); 17 | } 18 | app.show(); 19 | }, 20 | resetAllFile:function () { 21 | this.files.length = 0; 22 | sourceFile.forEach(function (a,index) { 23 | _this.files.push(new File(a)) 24 | }); 25 | $event.emit('file:reset'); 26 | }, 27 | checkFocus:function (app) { 28 | this.apps.sort(function (a,b) { 29 | if(a===app){ 30 | b.blur(); 31 | return 1; 32 | }else if(b===app){ 33 | a.blur(); 34 | return -1; 35 | }else{ 36 | a.blur(); 37 | b.blur(); 38 | return 0; 39 | } 40 | 41 | }); 42 | }, 43 | checkClose:function () { 44 | util.arrayRemove(this.apps,function (a) { 45 | return a._close; 46 | }); 47 | }, 48 | }; 49 | 50 | $event.on('app:close',function () { 51 | _this.checkClose(); 52 | }); 53 | $event.on('app:focus',function (app) { 54 | _this.checkFocus(app); 55 | }); 56 | $event.on('mousedown:wallpaper',function () { 57 | _this.apps.forEach(function (app) { 58 | app._focus = false; 59 | }) 60 | }); 61 | $event.on('window:resize',function () { 62 | _this.apps.forEach(function (app) { 63 | app.checkLayout(); 64 | }) 65 | }); 66 | 67 | // var vm = new Vue({ 68 | // data:function () { 69 | // return { 70 | // files:_this.files 71 | // } 72 | // } 73 | // }); 74 | // vm.$watch(function () { 75 | // return JSON.stringify(this.files); 76 | // },function () { 77 | // storage.set('files',this.files); 78 | // }); 79 | 80 | module.exports = _this; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Desktop-simulation 2 | ====================== 3 | 4 | Demo: http://demo.ruosen.io/desktop-simulation/ 5 | 6 | ## Init 7 | 8 | ``` 9 | npm install 10 | ``` 11 | 12 | ## build 13 | 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ## dev 19 | 20 | ``` 21 | npm run dev 22 | ``` 23 | 24 | ## Add Custom Apps 25 | 26 | Write app component code in `src/components/apps/`, and add desktop icon data to `src/data/files.js`. 27 | 28 | 49 | 50 | Component (my-app.vue) example: 51 | 52 | ``` 53 | 58 | 59 | 60 | 61 | Hello World! 62 | 63 | 64 | 65 | 66 | 81 | ``` 82 | 83 | Icon data example: 84 | 85 | ``` 86 | { 87 | name:"MyApp", 88 | app:"my-app", // app component's file name 89 | icon:"", // defined in css .icon 90 | options:{ // options will be extended to App Class's instance , it can be a function that returns options object 91 | data:{ 92 | }, 93 | singleton:true, 94 | width: function () { // value can be a function that returns dynamic value; 95 | return $(window).width() * 0.8; 96 | } 97 | } 98 | } 99 | ``` -------------------------------------------------------------------------------- /src/views/wallpaper.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/views/mouse-menu.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/views/selection.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/views/taskbar.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 54 | 55 | 61 | {{app.title}} 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/components/file-item.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 53 | 60 | 61 | 62 | 63 | 64 | {{file.name}} 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/service/app.js: -------------------------------------------------------------------------------- 1 | var count = 0; 2 | 3 | function delay(cb,n){ 4 | setTimeout(cb,n||0); 5 | } 6 | 7 | function App(o) { 8 | var _default = { 9 | _show: true, 10 | title: "app", 11 | icon:"", 12 | _focus: false, 13 | titleHeight:30, 14 | type: null, 15 | top: null, 16 | left: null, 17 | height: 400, 18 | width: 600, 19 | data:null, 20 | animating:false, 21 | maximized: false, 22 | oldPosition: null, 23 | resizable:true, 24 | singleton:false, 25 | _close:false 26 | }; 27 | $.extend(_default,o); 28 | $.each(_default,function (k,v) { 29 | if(typeof v == 'function'){ 30 | _default[k] = v(); 31 | } 32 | }); 33 | $.extend(this,_default); 34 | 35 | this.sortKey = count++; 36 | this.checkLayout(); 37 | } 38 | 39 | App.prototype.checkLayout = function () { 40 | var maxWidth = window._w; 41 | var maxHeight = window._h - this.titleHeight; 42 | 43 | if(this.width>maxWidth){ 44 | this.width = maxWidth; 45 | } 46 | if(this.height>maxHeight){ 47 | this.height=maxHeight; 48 | } 49 | 50 | if(this.left===null){ 51 | this.left = ( window._w - this.width ) / 2; 52 | } 53 | if(this.top===null){ 54 | this.top = ( window._h - this.height ) / 2; 55 | } 56 | 57 | 58 | if(this.left + this.width > window._w){ 59 | this.left = window._w - this.width; 60 | } 61 | if(this.height + this.titleHeight + this.top > window._h){ 62 | this.top = window._h - this.titleHeight - this.height; 63 | } 64 | 65 | if(this.maximized){ 66 | this.top = 0; 67 | this.left = 0; 68 | this.height = window._h - this.titleHeight; 69 | this.width = window._w; 70 | } 71 | } 72 | 73 | App.prototype.set = function (key,value) { 74 | var isResize = ['height','width','top','left'].indexOf(key)>=0; 75 | if(isResize){ 76 | this.maximized = false; 77 | 78 | if(this.resizable){ 79 | this[key] = value; 80 | } 81 | }else{ 82 | this[key] = value; 83 | } 84 | } 85 | 86 | App.prototype.show = function () { 87 | this._show = true; 88 | this.focus(); 89 | } 90 | 91 | App.prototype.hide = function () { 92 | this._show = false; 93 | this.blur(); 94 | }; 95 | 96 | App.prototype.close = function () { 97 | if(!this._close){ 98 | this._close = true; 99 | $event.emit('app:close',this); 100 | } 101 | } 102 | 103 | App.prototype.focus = function () { 104 | if(!this._focus){ 105 | this._focus = true; 106 | $event.emit('app:focus',this); 107 | } 108 | } 109 | 110 | App.prototype.blur = function () { 111 | this._focus = false; 112 | }; 113 | 114 | App.prototype.maximize = function (force) { 115 | var _this = this; 116 | if(!force && _this.maximized && _this.oldPosition){ 117 | _this.animating = true; 118 | delay(function () { 119 | _this.maximized = false; 120 | 121 | _this.top = _this.oldPosition.top; 122 | _this.left = _this.oldPosition.left; 123 | _this.height = _this.oldPosition.height; 124 | _this.width = _this.oldPosition.width; 125 | 126 | _this.oldPosition = null; 127 | delay(function () { 128 | _this.animating = false; 129 | },500) 130 | }) 131 | 132 | }else{ 133 | _this.animating = true; 134 | delay(function () { 135 | _this.maximized = true; 136 | _this.oldPosition = { 137 | top:_this.top, 138 | left:_this.left, 139 | height:_this.height, 140 | width:_this.width 141 | }; 142 | _this.top = 0; 143 | _this.left = 0; 144 | _this.height = window._h - _this.titleHeight; 145 | _this.width = window._w; 146 | delay(function () { 147 | _this.animating = false; 148 | },500); 149 | }) 150 | } 151 | }; 152 | 153 | module.exports = App; -------------------------------------------------------------------------------- /src/service/storage.js: -------------------------------------------------------------------------------- 1 | var pluses = /\+/g; 2 | 3 | function encode(s) { 4 | return _cookie.raw ? s : encodeURIComponent(s); 5 | } 6 | 7 | function decode(s) { 8 | return _cookie.raw ? s : decodeURIComponent(s); 9 | } 10 | 11 | function stringifyCookieValue(value) { 12 | return encode(_cookie.json ? JSON.stringify(value) : String(value)); 13 | } 14 | 15 | function parseCookieValue(s) { 16 | if (s.indexOf('"') === 0) { 17 | s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); 18 | } 19 | try { 20 | s = decodeURIComponent(s.replace(pluses, ' ')); 21 | return _cookie.json ? JSON.parse(s) : s; 22 | } catch(e) {} 23 | } 24 | function read(s, converter) { 25 | var value = _cookie.raw ? s : parseCookieValue(s); 26 | return $.isFunction(converter) ? converter(value) : value; 27 | } 28 | var _cookie = function (key, value, options) { 29 | // Write 30 | if (arguments.length > 1 && !$.isFunction(value)) { 31 | options = $.extend({}, _cookie.defaults, options); 32 | if (typeof options.expires === 'number') { 33 | var days = options.expires, t = options.expires = new Date(); 34 | t.setMilliseconds(t.getMilliseconds() + days * 864e+5); 35 | } 36 | return (document.cookie = [ 37 | encode(key), '=', stringifyCookieValue(value), 38 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 39 | options.path ? '; path=' + options.path : '', 40 | options.domain ? '; domain=' + options.domain : '', 41 | options.secure ? '; secure' : '' 42 | ].join('')); 43 | } 44 | var result = key ? undefined : {}, 45 | cookies = document.cookie ? document.cookie.split('; ') : [], 46 | i = 0, 47 | l = cookies.length; 48 | for (; i < l; i++) { 49 | var parts = cookies[i].split('='), 50 | name = decode(parts.shift()), 51 | cookie = parts.join('='); 52 | if (key === name) { 53 | result = read(cookie, value); 54 | break; 55 | } 56 | if (!key && (cookie = read(cookie)) !== undefined) { 57 | result[name] = cookie; 58 | } 59 | } 60 | return result; 61 | }; 62 | 63 | var originStorage = (function(){ 64 | var cookieKeyPrefix = "__localStorage__"; 65 | var supportLocalStorage = true; 66 | try{ 67 | window.localStorage.setItem('__test__',1); 68 | window.localStorage.getItem('__test__'); 69 | window.localStorage.removeItem('__test__'); 70 | }catch(e){ 71 | supportLocalStorage = false; 72 | } 73 | 74 | if( supportLocalStorage ){ 75 | return { 76 | get:function(key){ 77 | return window.localStorage.getItem(key); 78 | }, 79 | set:function(key,value){ 80 | return window.localStorage.setItem(key,value); 81 | }, 82 | clear:function(){ 83 | return window.localStorage.clear(); 84 | }, 85 | remove:function(key){ 86 | return window.localStorage.removeItem(key); 87 | } 88 | } 89 | }else{ 90 | return { 91 | get:function(key){ 92 | return _cookie(cookieKeyPrefix+key); 93 | }, 94 | set:function(key,value){ 95 | return _cookie(cookieKeyPrefix+key,value,{ 96 | expires:365 97 | }); 98 | }, 99 | clear:function(){ 100 | var cookies = document.cookie.split(";"); 101 | for (var i = 0; i < cookies.length; i++){ 102 | var key = cookies[i].split("=")[0]; 103 | _cookie(key,"",{ 104 | expires:-1 105 | }) 106 | } 107 | 108 | }, 109 | remove:function(key){ 110 | return _cookie(cookieKeyPrefix+key,"",{ 111 | expires:-1 112 | }) 113 | } 114 | } 115 | } 116 | })(); 117 | 118 | module.exports = { // 可以存对象,不限于字符串。 119 | get:function(k){ 120 | var a = originStorage.get(k); 121 | var _return; 122 | try{ 123 | var json = JSON.parse(a); 124 | _return = json.a; 125 | }catch(e){ 126 | _return = undefined; 127 | } 128 | return _return; 129 | }, 130 | set:function(k,v){ 131 | var o = {a:v}; 132 | originStorage.set(k,JSON.stringify(o)); 133 | }, 134 | clear:function(){ 135 | return originStorage.clear(); 136 | }, 137 | remove:function(k){ 138 | return originStorage.remove(k); 139 | } 140 | } -------------------------------------------------------------------------------- /src/components/apps/browser.vue: -------------------------------------------------------------------------------- 1 | 71 | 72 | 73 | 74 | 75 | < 76 | > 77 | c 78 | 79 | 80 | 81 | 88 | 89 | Go 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/components/apps/calculator.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 37 | 38 | clear 39 | 40 | {{computeSymbol[0]}} 41 | {{currentValue}} 42 | 43 | 44 | {{b[0]||b}} 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/views/file.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/components/app-window.vue: -------------------------------------------------------------------------------- 1 | 222 | 223 | 240 | 245 | 246 | 247 | 248 | {{app.title}} 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 265 | 266 | 267 | --------------------------------------------------------------------------------
目标:
{{ openedFile.options.data | json }}